Preciso citar as substituições de comandos ao atribuir sua saída a uma variável? [duplicado]

6

Eu costumo citar as substituições de comandos como mostrado abaixo, mesmo quando atribuindo sua saída a uma variável:

var="$(command)"

Isso é realmente necessário embora? Quando isso quebra? A resposta aceita aqui afirma:

DIRNAME="$(dirname $FILE)" will not do what you want if $FILE contains whitespace or globbing characters [?*.

O link aponta para a ótima página do Gray Cat Wiki sobre cotação, mas essa página não menciona citações de substituição de comandos especificamente. E enquanto citar a variável é claramente necessário, citando a substituição do comando em si não parece ser.

No entanto, o mesmo post conclui com:

DIRNAME="$(dirname "$FILE")" is the recommended way. You can replace DIRNAME= with a command and a space without changing anything else, and dirname receives the correct string.

Qual é o que eu sempre pensei também e muitas vezes ocrevei postagens aqui que não citavam isso. No entanto, a página wiki ligada acima também afirma que:

There are a few cases where double quotes may be safely omitted:

On the right hand side of a simple assignment. You may write foo=$bar without quotes. This is POSIX compliant.

[. . . ]

Embora var=$(command) não seja realmente uma tarefa "simples", não pude encontrar um caso em que as citações fossem realmente necessárias:

$ var=$(echo "foo bar baz")  ## whitespace works
$  echo "$var"
foo bar baz

$ var=$(printf "foo\nbar * baz") ## so do globbing characters
$ echo "$var"
foo
bar * baz

$ var1="foo\nbar * baz"
$ var=$(printf "$var1")  ## printing a variable doesn't make any difference
$ echo "$var" 
foo
bar * baz

$ var=$(printf '%s\n' "$var1")
$ echo "$var"
foo\nbar * baz

$ var=$(printf -- '-e %s\n' "$var1") ## strings starting with - also work
$ echo "$var"
-e foo\nbar * baz

Naturalmente, as aspas são absolutamente necessárias se a substituição de comandos estiver sendo usada diretamente para itens como command1 "$(command2)" , mas esse não parece ser o caso ao atribuir a uma variável.

Então, o que estou perdendo? As citações são necessárias? Qual caso citará citando uma substituição de comando ao atribuir seu valor de retorno a uma variável para protegê-lo? Ou é sempre OK não citar uma substituição de comando se for o lado direito de uma operação de atribuição de variável?

    
por terdon 29.04.2017 / 17:22

2 respostas

3

Como uma referência, o manual do Bash é claro neste :

A variable may be assigned to by a statement of the form

name=[value]

If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal (detailed below). [...] Word splitting is not performed, with the exception of "$@" as explained below. Filename expansion is not performed.

Nenhuma divisão de palavras, nenhuma expansão de nome de arquivo, portanto, não há necessidade de citações.

Quanto ao POSIX, seção 2.9.1 Comandos simples :

2. The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.
[...]
4. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.

Não tenho certeza se isso deve ser interpretado como significando que a divisão de campo acontece apenas para expansões feitas na etapa 2? Etapa 4: não menciona a divisão de campo, embora a seção Divisão de campo também não menciona atribuições de variáveis como uma exceção à produção de vários campos.

    
por 30.04.2017 / 00:32
4

Você não precisa para citar a expressão no lado direito de uma tarefa.

O que está irritando você é que a outra resposta recomenda para citar, no entanto. Mas isso é apenas sobre manutenção de código.

Considere o seguinte exemplo de correto :

DIRNAME=$(dirname "$FILE")
echo "debug: dirname is $DIRNAME"
ls "$DIRNAME"

Agora, depois de um tempo usando este script, você pode pensar que a mensagem de depuração pode ser removida. Então, usando um editor, você removerá a linha de eco. Então você notará que você nem precisa mais da variável DIRNAME e simplesmente move o comando ls para substituir o site da esquerda da tarefa. Agora você pode esquecer de adicionar as citações necessárias e terminar com este script quebrado :

ls $(dirname "$FILE")

A probabilidade de tal erro é ainda maior se o primeiro autor for um especialista em shell, mas o segundo editor for um novato.

É claro que é discutível se essa recomendação para evitar um recurso de shell portátil é realmente útil. Pessoalmente eu faço principalmente como ele recomenda. Eu também faço isso para as tarefas mais "simples" como: var="${foo}" (incluindo chaves supérfluas também).

    
por 29.04.2017 / 19:12