Expande uma variável cujo valor contém uma opção e um nome de caminho com espaços em branco

0
$ md="-l /tmp/test/my dir"
$ ls "$md"
ls: invalid option -- ' '
Try 'ls --help' for more information.

$ md="-l \"/tmp/test/my dir\""
$ ls "$md"
ls: invalid option -- ' '
Try 'ls --help' for more information.

Eu estava me perguntando por que ambos não funcionam? Obrigado. Note que o valor da variável contém

  • uma opção -l
  • um argumento de caminho que contém um espaço
  • um espaço que separa os dois acima.

Meu problema real é: eu escrevi um script myscript.sh

#! /bin/bash

old_dest=""
while getopts ":l:t" opt; do
    case $opt in
        l)
            old_dest="--link-dest \"$OPTARG\""
            ;;
    esac
done

rsync -a --delete "$old_dest" /path/to/source  /path/to/dest

Eu quero chamar o script como um comando com a opção -l .

myscript.sh -l "/media/t/my external hdd/backup/old one"

Como devo escrever a parte do script que atribui a old_dest ?

    
por Tim 16.05.2018 / 00:49

2 respostas

2

O motivo pelo qual seu comando não funciona é porque a remoção de citações / a divisão de palavras não é aplicada recursivamente.

Então, "$old_dest" é expandido para o argumento SINGLE a seguir:

--link-dest "/media/t/my external hdd/backup/old one"

Tudo isso é UM argumento, porque a variável estava entre aspas duplas. (E essas aspas duplas são removidas, mas as aspas duplas que resultaram da expansão , ou seja, faziam parte do valor da variável, não são removidas.)

Se você usou $old_dest , você receberia vários argumentos, mas não da maneira que deseja. Você receberia os seguintes cinco argumentos:

--link-dest
"/media/t/my
external
hdd/backup/old
one"

Isso ocorre porque você obteria expansão de variável seguida de divisão de palavras, conforme abordado no manual. Você não obteria expansão de variáveis seguida de interpretação das aspas na variável. E as aspas não seriam removidas, então o argumento final seria os quatro caracteres: one" .

O jeito certo de fazer isso, como coberto em outra resposta, é usar matrizes Bash.

Em uma nota separada, você pode gostar de como o Tcl lida com citações e divisão de palavras. Provavelmente é mais intuitivo, embora eu seja tão experiente com a divisão de palavras shell que tenho que pensar mais em Tcl do que em shell.

    
por 16.05.2018 / 04:20
2

Como você está usando o bash, e como o bash suporta arrays, sugiro fazer old_dest um array:

#! /bin/bash

old_dest=()
while getopts ":l:t" opt; do
    case $opt in
        l)
            old_dest+=('--link-dest' "$OPTARG")
            ;;
    esac
done

printf 'Element: %s\n' rsync -a --delete "${old_dest[@]}" /path/to/source  /path/to/dest

Eu instrumentei a chamada para rsync para ser uma chamada para printf para mostrar o comando e cada argumento em uma linha separada. Quando executado:

./myscript.sh -l "/media/t/my external hdd/backup/old one"

O resultado é:

Element: rsync
Element: -a
Element: --delete
Element: --link-dest
Element: /media/t/my external hdd/backup/old one
Element: /path/to/source
Element: /path/to/dest
    
por 16.05.2018 / 03:02