Resultado inesperado de a = “$ @”

6

Estou lutando com essa situação:

$ set -- 1 2 3
$ a="$@"
$ echo "$a"
1 2 3

O que eu acho inesperado é a tarefa em si.

man bash diz isso sobre a expansão "$@" :

When the expansion occurs within double quotes, each parameter expands to a separate word.

Então, isso deve ser análogo a:

b="1" "2" "3"
bash: 2: command not found

E é para isso que a expansão "$*" serve, eu entendo:

When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable. That is, "$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.

E isso deve estar correto:

$ set -- 1 2 3
$ a="$*"
$ echo "$a"
1 2 3

Então, por que "$@" produz o mesmo? Eles deveriam diferir neste ponto. Isso é um problema de Bash ou meu mal-entendido?

Shellcheck detecta isso como SC2124 . Eu também posso fornecer um exemplo que aciona SC2145 .

É observado em:

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02) x86_64 GNU/Linux
    
por Tomasz 18.05.2018 / 18:50

1 resposta

8

Tanto quanto eu posso dizer, POSIX deixa $@ em uma atribuição indefinida , então não é realmente um bug, exceto talvez na documentação do Bash. $@ é definido em dois casos:

  • When the expansion occurs in a context where field splitting will be performed...
  • When the expansion occurs within double-quotes, the behavior is unspecified unless [...] Field splitting would be performed [...] (*)

Mas,

In all other contexts the results of the expansion are unspecified.

A divisão de campo não acontece em uma atribuição e não aconteceria mesmo sem as aspas duplas, o que é indefinido.

Agora, suponho que a="$@" atue da mesma maneira que a="$*" , porque expandir para várias palavras não faria sentido algum aqui. Você não pode atribuir várias palavras a uma variável regular como entidades distintas, e atribuir uma, mas usando o resto como argumentos de comando, seria confuso e propenso a erros.

Como essa página de verificação de shell diz, o comportamento de "$@" em uma atribuição é diferente entre os shells. Bash e ksh unem os parâmetros posicionais com espaços, zsh e traço com a primeira letra de IFS (exatamente como "$*" faria).

$ bash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ ksh93 -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ zsh -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>
$ dash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>

Provavelmente, é melhor usar a="$*" se você quiser unir-se a uma única string ou escrever explicitamente o que quiser.

(* ou outro caso envolvendo ${parameter:-word} de expansões)

    
por 18.05.2018 / 19:06