Acessando a linha de comando do bash args $ @ vs $ *

327

Em muitas perguntas do SO e tutoriais do bash, vejo que posso acessar os argumentos da linha de comando nos scripts do bash de duas maneiras:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

O que resulta em:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

Qual é a diferença entre $*e $@?
Quando se deve usar o primeiro e quando se deve usar o último?

Oz123
fonte
dê uma olhada nesta resposta: stackoverflow.com/a/842325/671366
codeling 7/12
análise estática no IntelliJ trata echo "something $@"como um erro
Alex Cohn

Respostas:

437

A diferença aparece quando os parâmetros especiais são citados. Deixe-me ilustrar as diferenças:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

mais um exemplo da importância de citar: observe que existem 2 espaços entre "arg" e o número, mas se eu não citar $ word:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

e no bash, "$@"é a lista "padrão" para iterar sobre:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3
Glenn Jackman
fonte
65
Sempre achei que esse conceito era melhor demonstrado por um exemplo simples, no qual o manual do bash está completamente ausente.
chepner
5
Existe um possível caso de uso, quando $*ou "$*"pode ser necessário, e o objetivo não pode ser atendido por $@ou "$@"?
anishsane
5
Qual versão é mais adequada para um script "wrapper", em que os parâmetros dos scripts precisam se tornar parâmetros para um novo comando?
Segfault
7
@Segfault, nesse caso, sempre escolha "$@"entre aspas.
Glenn Jackman
2
Esta resposta contém exemplos úteis, mas seria melhor se também explicasse o mecanismo por trás deles. Por que funciona assim?
Lii 28/07
255

Uma boa tabela de visão geral acessível do Bash Hackers Wiki :

Tabela $ * versus $ @

onde cna terceira linha está o primeiro caractere $IFS, o Internal Field Separator, uma variável de shell.

Se os argumentos devem ser armazenados em uma variável de script e se espera que os argumentos contenham espaços, recomendo de todo o coração empregar um "$*"truque com o separador de campo interno $IFSdefinido como tab .

Serge Stroobandt
fonte
42
... onde "c" é o primeiro caractere do $ IFS
glenn jackman
39
... e $IFSsignifica "Separador de campo interno".
Serge Stroobandt
Aqui está um exemplo , que inclui entrada entre aspas. A entrada também importa!
Serge Stroobandt 17/10
Digamos que eu queira criar um script de wrapper que não faça nada além de imitar a funcionalidade do comando agrupado. Qual sintaxe devo usar para passar os argumentos do script wrapper para o comando interno?
Marinos Um
44

$ *

Expande para os parâmetros posicionais, iniciando em um. Quando a expansão ocorre entre aspas duplas, ela se expande para uma única palavra com o valor de cada parâmetro separado pelo primeiro caractere da variável especial IFS. Ou seja, "$ *" é equivalente a "$ 1c $ 2c ...", em que c é o primeiro caractere do valor da variável IFS. Se o IFS não estiver definido, os parâmetros serão separados por espaços. Se o IFS for nulo, os parâmetros serão unidos sem separadores intervenientes.

$ @

Expande para os parâmetros posicionais, iniciando em um. Quando a expansão ocorre entre aspas duplas, cada parâmetro se expande para uma palavra separada. Ou seja, "$ @" é equivalente a "$ 1" "$ 2" ... Se a expansão de aspas duplas ocorrer dentro de uma palavra, a expansão do primeiro parâmetro será associada à parte inicial da palavra original e a expansão do último parâmetro é associado à última parte da palavra original. Quando não há parâmetros posicionais, "$ @" e $ @ expandem para nada (ou seja, eles são removidos).

Fonte: Bash man

Muffo
fonte
15

$ @ é igual a $ *, mas cada parâmetro é uma string entre aspas, ou seja, os parâmetros são transmitidos intactos, sem interpretação ou expansão. Isso significa, entre outras coisas, que cada parâmetro na lista de argumentos é visto como uma palavra separada.

Obviamente, "$ @" deve ser citado.

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

rkosegi
fonte
1

Este exemplo permite destacar as diferenças entre "at" e "asterix" enquanto as usamos. Eu declarei duas matrizes "frutas" e "vegetais"

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "${fruits[*]}"            
printf "Fruits:\t%s\n" "${fruits[@]}"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "${vegetables[*]}"    
printf "Vegetables:\t%s\n" "${vegetables[@]}"    

Veja o seguinte resultado no código acima:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion
stefansson
fonte
7
Cientificamente falando, o tomate é uma fruta.
Randy
1
Você tem direito! "Na botânica, uma fruta é a estrutura geradora de sementes nas plantas com flores (também conhecidas como angiospermas) formadas a partir do ovário após a floração". en.wikipedia.org/wiki/Fruit
stefansson 26/03/19
@ Randy: cientificamente falando, todas as frutas são vegetais (é sinônimo de "planta").
Cris Luengo
@CrisLuengo heresy! :)
Randy