Quando não são citados, $*
e $@
são iguais. Você não deve usar nenhum desses, porque eles podem quebrar inesperadamente assim que você tiver argumentos contendo espaços ou curingas.
"$*"
expande para uma única palavra "$1c$2c..."
. Geralmente c
é um espaço, mas na verdade é o primeiro caractere de IFS
, então pode ser qualquer coisa que você escolher.
O único bom uso que encontrei para ele é:
associa argumentos com vírgula (versão simples)
join1() {
IFS=,
echo "$*"
}
join1 a b c # => a,b,c
associa argumentos com o delimitador especificado (versão melhorada)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
se expande para separar palavras: "$1"
"$2"
...
Isso é quase sempre o que você quer. Ele expande cada parâmetro posicional para uma palavra separada, o que o torna perfeito para usar argumentos de linha de comando ou de função e depois passá-los para outro comando ou função. E porque ele se expande usando aspas duplas, isso significa que as coisas não quebram se, digamos, "$1"
contiver um espaço ou um asterisco ( *
).
Vamos escrever um script chamado svim
que executa vim
com sudo
. Vamos fazer três versões para ilustrar a diferença.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Todos estarão bem para casos simples, por ex. um único nome de arquivo que não contém espaços:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Mas apenas $*
e "$@"
funcionam corretamente se você tiver vários argumentos.
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
E somente "$*"
e "$@"
funcionarão corretamente se você tiver argumentos contendo espaços.
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Portanto, apenas "$@"
funcionará corretamente o tempo todo.
typeset
é como criar uma variável local em ksh
( bash
e ash
use local
). Isso significa que IFS
será restaurado para seu valor anterior quando a função retornar. Isso é importante, porque os comandos executados posteriormente podem não funcionar corretamente se IFS
estiver definido como algo fora do padrão.