Quand ils ne sont pas cités $*
et $@
sont les mêmes. Vous ne devez utiliser ni l'un ni l'autre, car ils peuvent se rompre de manière inattendue dès que vous avez des arguments contenant des espaces ou des caractères génériques.
"$*"
se développe en un seul mot "$1c$2c..."
. Généralement, il c
s’agit d’un espace, mais c’est en fait le premier caractère de IFS
votre choix.
Le seul bon usage que j'ai jamais trouvé est:
joindre des arguments avec une virgule (version simple)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
rejoindre des arguments avec le délimiteur spécifié (meilleure version)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
se développe pour séparer les mots: "$1"
"$2"
...
C'est presque toujours ce que tu veux. Il étend chaque paramètre de position en un mot distinct, ce qui le rend idéal pour prendre des arguments de ligne de commande ou de fonction et les transmettre ensuite à une autre commande ou fonction. Et comme il utilise davantage les guillemets, les choses ne se cassent pas si, par exemple, "$1"
contient un espace ou un astérisque ( *
).
Écrivons un script appelé svim
qui fonctionne vim
avec sudo
. Nous allons faire trois versions pour illustrer la différence.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Tous ces éléments conviendront pour des cas simples, par exemple un nom de fichier unique ne contenant pas d'espaces:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Mais seulement $*
et "$@"
fonctionne correctement si vous avez plusieurs arguments.
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"
Et seulement "$*"
et "$@"
fonctionnent correctement si vous avez des arguments contenant des espaces.
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"
Donc, seul "$@"
fonctionnera correctement tout le temps.
typeset
est comment créer une variable locale ksh
( bash
et l’ ash
utiliser à la local
place). Cela signifie que IFS
sa valeur précédente sera rétablie lorsque la fonction reviendra. Ceci est important, car les commandes que vous exécuterez par la suite risquent de ne pas fonctionner correctement si vous IFS
définissez un paramètre non standard.
$*
. Je pensais toujours que c'était complètement inutile ... joindre avec un délimiteur est un bon cas d'utilisation.Réponse courte: utilisez
"$@"
(notez les guillemets). Les autres formes sont très rarement utiles."$@"
est une syntaxe assez étrange. Il est remplacé par tous les paramètres de position, sous forme de champs distincts. S'il n'y a pas de paramètre de position ($#
vaut 0), alors se"$@"
développe jusqu'à rien (pas une chaîne vide, mais une liste avec 0 éléments), s'il existe un paramètre de position"$@"
équivalent à"$1"
, s'il existe deux paramètres de position"$@"
équivalent à"$1" "$2"
, etc."$@"
vous permet de transmettre les arguments d'un script ou d'une fonction à une autre commande. Il est très utile pour les wrappers effectuant des tâches telles que la définition de variables d'environnement, la préparation de fichiers de données, etc. avant d'appeler une commande avec les mêmes arguments et options que ceux utilisés pour le wrapper.Par exemple, la fonction suivante filtre la sortie de
cvs -nq update
. Outre le filtrage de sortie et le statut de retour (qui est celui degrep
plutôt que celui decvs
), l'appelcvssm
de certains arguments se comporte comme un appelcvs -nq update
avec ces arguments."$@"
étend à la liste des paramètres de position. Dans les shells qui prennent en charge les tableaux, il existe une syntaxe similaire pour développer la liste des éléments du tableau:"${array[@]}"
(les accolades sont obligatoires sauf dans zsh). Là encore, les guillemets doubles sont quelque peu trompeurs: ils protègent contre la division des champs et la génération de motifs des éléments du tableau, mais chaque élément du tableau se termine dans son propre champ.Certains anciens coquillages avaient ce qu’on pourrait appeler un bogue: quand il n’y avait pas d’arguments de position, ils étaient
"$@"
étendus à un seul champ contenant une chaîne vide plutôt qu’à aucun champ. Cela a conduit à la solution de contournement${1+"$@"}
(rendue célèbre via la documentation Perl ). Seules les versions plus anciennes du shell Bourne et de l'implémentation OSF1 sont concernées, aucun de ses remplacements compatibles actuels (ash, ksh, bash,…) ne le sont./bin/sh
n'est pas affecté par les systèmes publiés au 21ème siècle que je connaisse (à moins que vous ne comptiez la version de maintenance Tru64, et même s'il/usr/xpg4/bin/sh
existe en toute sécurité, seuls les#!/bin/sh
scripts sont affectés, pas les#!/usr/bin/env sh
scripts tant que votre PATH est configuré pour la conformité POSIX) . En bref, il s’agit d’une anecdote historique dont vous ne devez pas vous inquiéter."$*"
s'étend toujours à un mot. Ce mot contient les paramètres de position, concaténés avec un espace entre eux. (Plus généralement, le séparateur est le premier caractère de la valeur de laIFS
variable. Si la valeur deIFS
est la chaîne vide, le séparateur est la chaîne vide.) S'il n'y a pas de paramètre de position,"$*"
la chaîne est vide, s'il y en a deux paramètres de position etIFS
sa valeur par défaut est alors"$*"
équivalente à"$1 $2"
, etc.$@
et les$*
citations extérieures sont équivalentes. Ils développent la liste des paramètres de position, sous forme de champs séparés, comme"$@"
; mais chaque champ résultant est ensuite divisé en champs distincts qui sont traités comme des modèles de caractère générique de nom de fichier, comme d'habitude avec les extensions de variable non citées.Par exemple, si le répertoire actuel contient trois fichiers
bar
,baz
etfoo
alors:la source
"$@"
s’est en fait étendue à une liste constituée de la chaîne vide: unix.stackexchange.com/questions/68484/…Voici un script simple pour démontrer la différence entre
$*
et$@
:Sortie:
Dans la syntaxe de tableau, il n'y a pas de différence lors de l'utilisation de
$*
ou$@
. Cela n'a de sens que lorsque vous les utilisez avec des guillemets doubles"$*"
et"$@"
.la source
IFS="^${IFS}"
, cependant?IFS
.IFS="^xxxxx"
ferait-on? Le${IFS}
suffixe de fin m'a fait penser que vous faisiez quelque chose de plus délicat, comme de récupérer automatiquement le fichier IFS d'origine à la fin (par exemple: le premier caractère décalé automatiquement ou quelque chose du genre).Le code que vous avez fourni donnera le même résultat. Pour mieux le comprendre, essayez ceci:
La sortie devrait maintenant être différente. Voici ce que je reçois:
Cela a fonctionné pour moi
bash
. Pour autant que je sache, ksh ne devrait pas différer beaucoup. Essentiellement, citer$*
traitera tout comme un seul mot et$@
traitera la liste comme des mots séparés, comme on peut le voir dans l'exemple ci-dessus.Comme exemple d'utilisation de la
IFS
variable avec$*
, considérons ceciJe reçois ceci comme résultat:
En outre, je viens de confirmer que cela fonctionne de la même manière
ksh
. Les deuxbash
etksh
testé ici étaient sous OSX mais je ne vois pas en quoi cela importait beaucoup.la source
unset IFS
à la fin pour le réinitialiser à l'original, mais cela n'a fonctionné pour moi aucun problème et cela aecho $IFS
eu pour résultat la sortie standard que j'en tire . Le fait deIFS
définir les accolades introduit une nouvelle portée. Par conséquent, si vous ne l’exportez pas, cela n’affectera pas l’extérieurIFS
.echo $IFS
ne prouve rien, car le shell voit le,
, mais fait ensuite le fractionnement de mots en utilisantIFS
! Essayezecho "$IFS"
.IFS
devrait résoudre cela.IFS
avoir une valeur personnalisée différente avant d'appeler la fonction. Mais oui, la plupart du temps, désactiver l'IFS fonctionnera.La différence est importante lors de l'écriture de scripts qui doivent utiliser les paramètres de position de la bonne manière ...
Imaginez l'appel suivant:
Ici, il n'y a que 4 paramètres:
Dans mon cas,
myuseradd
c'est juste un wrapper car iluseradd
accepte les mêmes paramètres, mais ajoute un quota pour l'utilisateur:Notez l'appel à
useradd "$@"
, avec$@
cité. Cela respectera les paramètres et les enverra en l'étatuseradd
. Si vous deviez donner un guillemet$@
(ou utiliser$*
également un guillemet), useradd verrait 5 paramètres, le 3ème paramètre contenant un espace serait scindé en deux:(et inversement, si vous deviez utiliser
"$*"
, useradd ne verriez un paramètre:-m -c Carlos Campderrós ccampderros
)Donc, en bref, si vous avez besoin de travailler avec des paramètres respectant les paramètres multi-mots, utilisez
"$@"
.la source
// homme bash . est ksh, un comportement similaire.
la source
Parler des différences entre
zsh
etbash
:Avec des guillemets autour
$@
et$*
,zsh
etbash
se comportent de la même manière, et je suppose que le résultat est assez standard parmi tous les obus:Sans les guillemets, les résultats sont les mêmes pour
$*
et$@
, mais sont différents dansbash
et danszsh
. Dans ce cas,zsh
montre un comportement étrange:(Zsh ne divise généralement pas les données textuelles à l'aide d'IFS, sauf demande explicite, mais remarque qu'ici l'argument vide manque de manière inattendue dans la liste.)
la source
$@
n'est pas spécial à cet égard:$x
étend au plus un mot, mais les variables vides développent à rien (pas un mot vide). Essayezprint -l a $foo b
avecfoo
vide ou indéfini.Une des réponses dit
$*
(que je considère comme un "splat") est rarement utile.Je recherche google avec
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Étant donné que les URL sont souvent divisés avec un
+
, mais mon clavier rendplus facile à atteindre que
+
,$*
+ se$IFS
sentir utile.la source