Quando não estão entre aspas $*
e $@
são iguais. Você não deve usar nenhum deles, pois eles podem quebrar inesperadamente assim que houver 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
, portanto pode ser qualquer coisa que você escolher.
O único bom uso que eu já encontrei para isso é:
juntar argumentos com vírgula (versão simples)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
juntar 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
"$@"
expande para separar palavras: "$1"
"$2"
...
Isso é quase sempre o que você deseja. Ele expande cada parâmetro posicional para uma palavra separada, o que o torna perfeito para receber argumentos de linha de comando ou função e depois passá-los para outro comando ou função. E como se expande usando aspas duplas, significa que as coisas não quebram se, digamos, "$1"
contiver um espaço ou um asterisco ( *
).
Vamos escrever um script chamado svim
que roda vim
com sudo
. Faremos 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 eles serão adequados para casos simples, por exemplo, 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 "$@"
funcione 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 "$@"
funcione corretamente se você tiver argumentos que contenham 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 ksh
( bash
e ash
usar local
). Isso significa IFS
que será restaurado ao seu valor anterior quando a função retornar. Isso é importante, porque os comandos executados posteriormente podem não funcionar corretamente se IFS
estiverem configurados para algo fora do padrão.
$*
. Eu sempre estava considerando que era completamente inútil ... juntar-se ao delimitador é um bom caso de uso.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 degrep
e não o decvs
), chamarcvssm
alguns argumentos se comporta como chamarcvs -nq update
com esses argumentos."$@"
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/sh
nã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/sh
seja seguro, apenas os#!/bin/sh
scripts serão afetados, e não os#!/usr/bin/env sh
scripts, 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 daIFS
variável. Se o valor deIFS
for 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 eIFS
seu 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
,baz
efoo
, em seguida:fonte
"$@"
de fato, expandir-se para um consisiting lista da cadeia vazia: unix.stackexchange.com/questions/68484/...Aqui está um script simples para demonstrar a diferença entre
$*
e$@
:Resultado:
Na sintaxe da matriz, não há diferença ao usar
$*
ou$@
. Só faz sentido quando você as usa com aspas duplas"$*"
e"$@"
.fonte
IFS="^${IFS}"
?IFS
.IFS="^xxxxx"
faria? O${IFS}
sufixo à direita me fez pensar que você estava fazendo algo mais complicado, como de alguma forma recuperar automaticamente o IFS original no final (por exemplo: o primeiro caractere mudou automaticamente ou algo assim).O código que você forneceu dará o mesmo resultado. Para entender melhor, tente o seguinte:
A saída agora deve ser diferente. Aqui está o que eu recebo:
Isso funcionou para mim
bash
. Até onde eu sei, o ksh não deve diferir muito. Essencialmente, citar$*
tratará tudo como uma palavra e citará$@
a lista como palavras separadas, como pode ser visto no exemplo acima.Como um exemplo de uso da
IFS
variável with$*
, considere issoEu recebo isso como resultado:
Além disso, acabei de confirmar que funciona da mesma maneira
ksh
. Ambosbash
eksh
testados aqui estavam sob OSX, mas não vejo como isso importaria muito.fonte
unset IFS
final para redefini-lo ao original, mas funcionou para mim sem problemas eecho $IFS
resultou na saída padrão que obtive dele. Definir osIFS
colchetes dentro de um encargo apresenta um novo escopo; portanto, a menos que você o exporte, isso não afetará o exteriorIFS
.echo $IFS
não prova nada, porque o shell vê o,
, mas faz a divisão de palavras usandoIFS
! Tenteecho "$IFS"
.IFS
deve resolver isso.IFS
tenha um valor personalizado diferente antes de chamar a função. Mas sim, na maioria das vezes, a desabilitação do IFS funcionará.A diferença é importante ao escrever scripts que devem usar os parâmetros posicionais da maneira correta ...
Imagine a seguinte chamada:
Aqui existem apenas 4 parâmetros:
No meu caso,
myuseradd
é apenas um invólucro parauseradd
que aceita os mesmos parâmetros, mas adiciona uma cota para o usuário:Observe a chamada para
useradd "$@"
, com$@
citado. Isso respeitará os parâmetros e os enviará como estãouseradd
. Se você tivesse que citar$@
(ou usar$*
também não citado), o useradd veria 5 parâmetros, pois o terceiro parâmetro que continha um espaço seria dividido em dois:(e vice-versa, se você fosse usar
"$*"
, useradd iria ver apenas um parâmetro:-m -c Carlos Campderrós ccampderros
)Portanto, resumindo, se você precisar trabalhar com parâmetros que respeitem parâmetros de várias palavras, use
"$@"
.fonte
// homem bash . é ksh, justo, comportamento semelhante.
fonte
Falando sobre diferenças entre
zsh
ebash
:Com aspas ao redor
$@
e$*
,zsh
ebash
se comportam da mesma forma, acho que o resultado é bastante comum entre todas as conchas:Sem aspas, os resultados são os mesmos para
$*
e$@
, mas diferentes dentrobash
e dentrozsh
. Neste caso,zsh
mostra algum comportamento estranho:(O Zsh geralmente não divide dados textuais usando o IFS, a menos que seja solicitado explicitamente, mas observe que aqui o argumento vazio está inesperadamente ausente na lista.)
fonte
$@
não é especial nesse sentido:$x
expande para no máximo uma palavra, mas variáveis vazias se expandem para nada (não é uma palavra vazia). Tenteprint -l a $foo b
comfoo
vazio ou indefinido.Uma das respostas diz
$*
(que considero um "splat") raramente é útil.Eu pesquiso no google com
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Como os URLs geralmente são divididos com um
+
, mas meu tecladofacilita o alcance do que
+
,$*
+$IFS
vale a pena.fonte