Qual é a diferença entre “$ @” e “$ *” no Bash?

Parece-me que ambos armazenam todos os argumentos da linha de comando.

Então, há uma diferença entre os dois?

A diferença é sutil; "$*" cria um argumento separado pela variável $IFS , enquanto "$@" se expande em argumentos separados. Como exemplo, considere:

 for i in "$@"; do echo "@ '$i'"; done for i in "$*"; do echo "* '$i'"; done 

Quando executado com vários argumentos:

 ./testvar foo bar baz 'long arg' @ 'foo' @ 'bar' @ 'baz' @ 'long arg' * 'foo bar baz long arg' 

Para mais detalhes:

http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters

$ *

Expande para os parâmetros posicionais, a partir de 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 IFS for nulo, os parâmetros serão unidos sem os separadores intervenientes.

$ @

Expande para os parâmetros posicionais, a partir de 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 com aspas duplas ocorrer dentro de uma palavra, a expansão do primeiro parâmetro será unida à parte inicial da palavra original e expansão do último parâmetro é unida com a última parte da palavra original. Quando não há parâmetros posicionais, “$ @” e $ @ se expandem para nada (ou seja, são removidos).

Uma diferença fundamental do meu POV é que "$@" preserva o número original de argumentos. É a única forma que faz.

Por exemplo, se o arquivo my_script contiver:

 #!/bin/bash main() { echo 'MAIN sees ' $# ' args' } main $* main $@ main "$*" main "$@" ### end ### 

e eu corro assim:

 my_script 'abc' de 

Eu vou conseguir essa saída:

 MAIN sees 5 args MAIN sees 5 args MAIN sees 1 args MAIN sees 3 args