Resposta curta: use"$@" (observe as aspas duplas). As outras formas raramente são úteis.
"$@"é uma sintaxe bastante estranha. É substituído por todos os parâmetros posicionais, como campos separados. Se não houver parâmetros posicionais ( $#é 0), ele se "$@"expande para nada (não é uma sequência vazia, mas uma lista com 0 elementos); se houver um parâmetro posicional, "$@"é equivalente a "$1"; se houver dois parâmetros posicionais, "$@"é equivalente a "$1" "$2"etc.
"$@"permite passar os argumentos de um script ou função para outro comando. É muito útil para wrappers que fazem coisas como definir variáveis de ambiente, preparar arquivos de dados etc. antes de chamar um comando com os mesmos argumentos e opções com os quais o wrapper foi chamado.
Por exemplo, a função a seguir filtra a saída de cvs -nq update. Além da filtragem de saída e do status de retorno (que é o de grepe não o de cvs), chamar cvssmalguns argumentos se comporta como chamar cvs -nq updatecom esses argumentos.
cvssm () { cvs -nq update "$@" | egrep -v '^[?A]'; }
"$@"expande para a lista de parâmetros posicionais. Nos shells que suportam matrizes, existe uma sintaxe semelhante para expandir a lista de elementos da matriz: "${array[@]}"(os colchetes são obrigatórios, exceto no zsh). Novamente, as aspas duplas são um pouco enganadoras: elas protegem contra a divisão de campos e a geração de padrões dos elementos da matriz, mas cada elemento da matriz termina em seu próprio campo.
Algumas conchas antigas tinham o que é indiscutivelmente um bug: quando não havia argumentos posicionais, "$@"expandíamos para um único campo contendo uma string vazia, em vez de para nenhum campo. Isso levou à solução alternativa${1+"$@"} (que ficou famosa através da documentação do Perl ). Somente versões mais antigas do shell Bourne real e da implementação OSF1 são afetadas, nenhuma de suas substituições compatíveis modernas (ash, ksh, bash,…) são. /bin/shnão é afetado em nenhum sistema lançado no século XXI que eu conheça (a menos que você conte a versão de manutenção do Tru64, e mesmo que /usr/xpg4/bin/shseja seguro, apenas os #!/bin/shscripts serão afetados, e não os #!/usr/bin/env shscripts, desde que seu PATH esteja configurado para conformidade com POSIX) . Em resumo, esta é uma anedota histórica com a qual você não precisa se preocupar.
"$*"sempre se expande para uma palavra. Esta palavra contém os parâmetros posicionais, concatenados com um espaço intermediário. (De maneira mais geral, o separador é o primeiro caractere do valor da IFSvariável. Se o valor de IFSfor a sequência vazia, o separador será a sequência vazia.) Se não houver parâmetros posicionais, "$*"será a sequência vazia, se houver dois parâmetros posicionais e IFSseu valor padrão "$*"é equivalente a "$1 $2"etc.
$@e $*aspas externas são equivalentes. Eles se expandem para a lista de parâmetros posicionais, como campos separados, como "$@"; mas cada campo resultante é dividido em campos separados, que são tratados como padrões curinga de nome de arquivo, como de costume com expansões de variáveis não citadas.
Por exemplo, se o diretório atual contém três arquivos bar, baze foo, em seguida:
set -- # no positional parameters
for x in "$@"; do echo "$x"; done # prints nothing
for x in "$*"; do echo "$x"; done # prints 1 empty line
for x in $*; do echo "$x"; done # prints nothing
set -- "b* c*" "qux"
echo "$@" # prints `b* c* qux`
echo "$*" # prints `b* c* qux`
echo $* # prints `bar baz c* qux`
for x in "$@"; do echo "$x"; done # prints 2 lines: `b* c*` and `qux`
for x in "$*"; do echo "$x"; done # prints 1 lines: `b* c* qux`
for x in $*; do echo "$x"; done # prints 4 lines: `bar`, `baz`, `c*` and `qux`