Wenn sie nicht zitiert werden $*
und gleich $@
sind. Sie sollten keines von beiden verwenden, da sie unerwartet abbrechen können, sobald Sie Argumente haben, die Leerzeichen oder Platzhalter enthalten.
"$*"
wird zu einem einzigen Wort erweitert "$1c$2c..."
. Normalerweise c
ist es ein Leerzeichen, aber es ist tatsächlich das erste Zeichen von IFS
, also kann es alles sein, was Sie wählen.
Die einzige gute Verwendung, die ich jemals dafür gefunden habe, ist:
Argumente mit Komma verbinden (einfache Version)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
Argumente mit dem angegebenen Trennzeichen verbinden (bessere Version)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
erweitert, um Wörter zu trennen: "$1"
"$2"
...
Das ist fast immer was Sie wollen. Es erweitert jeden Positionsparameter zu einem eigenen Wort, wodurch es sich perfekt eignet, um Befehlszeilen- oder Funktionsargumente aufzunehmen und sie dann an einen anderen Befehl oder eine andere Funktion weiterzuleiten. Und weil es mit doppelten Anführungszeichen erweitert wird, bedeutet dies, dass Dinge nicht kaputt gehen, wenn sie beispielsweise "$1"
ein Leerzeichen oder ein Sternchen ( *
) enthalten.
Lassen Sie uns ein Skript namens schreiben svim
, das vim
mit läuft sudo
. Wir werden drei Versionen machen, um den Unterschied zu veranschaulichen.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Für einfache Fälle, z. B. einen einzelnen Dateinamen, der keine Leerzeichen enthält, sind alle in Ordnung:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Aber nur $*
und "$@"
richtig arbeiten, wenn Sie mehrere Argumente haben.
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"
Und nur "$*"
und "$@"
richtig funktionieren, wenn Sie Argumente haben, die Leerzeichen enthalten.
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"
So "$@"
funktioniert nur die ganze Zeit richtig.
typeset
So erstellen Sie eine lokale Variable in ksh
( bash
und ash
verwenden sie local
stattdessen). Es bedeutet IFS
, dass der vorherige Wert wiederhergestellt wird, wenn die Funktion zurückkehrt. Dies ist wichtig, da die Befehle, die Sie anschließend ausführen, möglicherweise nicht ordnungsgemäß funktionieren, wenn sie IFS
auf einen nicht standardmäßigen Wert festgelegt sind.
$*
. Ich habe immer gedacht, dass es völlig nutzlos ist. Join with Delimiter ist ein guter Anwendungsfall.Kurze Antwort: Verwenden Sie
"$@"
(beachten Sie die doppelten Anführungszeichen). Die anderen Formen sind sehr selten nützlich."$@"
ist eine ziemlich seltsame Syntax. Es wird durch alle Positionsparameter als separate Felder ersetzt. Wenn es keine Positionsparameter gibt ($#
ist 0), wird"$@"
zu nichts erweitert (keine leere Zeichenfolge, sondern eine Liste mit 0 Elementen). Wenn es einen Positionsparameter"$@"
gibt, entspricht dies"$1"
, wenn es zwei Positionsparameter"$@"
gibt, entspricht dies"$1" "$2"
, usw."$@"
Ermöglicht es Ihnen, die Argumente eines Skripts oder einer Funktion an einen anderen Befehl weiterzugeben. Dies ist sehr nützlich für Wrapper, die z. B. Umgebungsvariablen festlegen, Datendateien vorbereiten usw., bevor sie einen Befehl mit denselben Argumenten und Optionen aufrufen, mit denen der Wrapper aufgerufen wurde.Die folgende Funktion filtert beispielsweise die Ausgabe von
cvs -nq update
. Abgesehen von der Ausgabefilterung und dem Rückgabestatus (der dem vongrep
und nicht dem von entsprichtcvs
)cvssm
verhält sich das Aufrufen einiger Argumente wie das Aufrufencvs -nq update
mit diesen Argumenten."$@"
Erweitert die Liste der Positionsparameter. In Shells, die Arrays unterstützen, gibt es eine ähnliche Syntax, um die Liste der Elemente des Arrays zu erweitern:"${array[@]}"
(die geschweiften Klammern sind mit Ausnahme von zsh obligatorisch). Auch hier sind die Anführungszeichen etwas irreführend: Sie schützen vor Feldteilung und Mustererzeugung der Array-Elemente, aber jedes Array-Element landet in einem eigenen Feld.Einige alte Muscheln hatten vermutlich einen Fehler: Wenn es keine Positionsargumente gab, wurden
"$@"
sie auf ein einzelnes Feld erweitert, das eine leere Zeichenfolge enthielt, und nicht auf ein Feld. Dies führte zur Problemumgehung${1+"$@"}
( bekannt geworden durch die Perl-Dokumentation ). Es sind nur ältere Versionen der eigentlichen Bourne-Shell und der OSF1-Implementierung betroffen, keine der modernen kompatiblen Ersetzungen (ash, ksh, bash,…)./bin/sh
ist auf keinem System betroffen, das im 21. Jahrhundert auf den Markt gebracht wurde (es sei denn, Sie zählen die Tru64-Wartungsversion und es/usr/xpg4/bin/sh
ist auch sicher, dass nur Skripte#!/bin/sh
betroffen sind, keine#!/usr/bin/env sh
Skripte, solange Ihr PATH für die POSIX-Kompatibilität eingerichtet ist). . Kurz gesagt, dies ist eine historische Anekdote, über die Sie sich keine Sorgen machen müssen."$*"
wird immer zu einem Wort erweitert. Dieses Wort enthält die Positionsparameter, die mit einem Leerzeichen dazwischen verkettet sind. (Im Allgemeinen ist das Trennzeichen das erste Zeichen des Werts derIFS
Variablen. Wenn der Wert vonIFS
die leere Zeichenfolge ist, ist das Trennzeichen die leere Zeichenfolge.) Wenn keine Positionsparameter vorhanden sind,"$*"
ist die leere Zeichenfolge, wenn zwei vorhanden sind Positionsparameter undIFS
hat einen Standardwert dann"$*"
entspricht"$1 $2"
usw.$@
und$*
äußere Anführungszeichen sind gleichwertig. Sie erweitern die Liste der Positionsparameter als separate Felder, z"$@"
. Jedes resultierende Feld wird dann jedoch in separate Felder aufgeteilt, die wie üblich mit nicht zitierten Variablenerweiterungen als Platzhaltermuster für Dateinamen behandelt werden.Wenn das aktuelle Verzeichnis beispielsweise drei Dateien enthält
bar
,baz
undfoo
dann:quelle
"$@"
tatsächlich eine Liste erweitert, die aus der leeren Zeichenfolge besteht: unix.stackexchange.com/questions/68484/…Hier ist ein einfaches Skript, um den Unterschied zwischen
$*
und zu demonstrieren$@
:Ausgabe:
In der Array-Syntax gibt es keinen Unterschied, wenn Sie
$*
oder verwenden$@
. Es macht nur Sinn, wenn Sie sie mit doppelten Anführungszeichen"$*"
und verwenden"$@"
.quelle
IFS="^${IFS}"
?IFS
.IFS="^xxxxx"
würde das tun? Das abschließende${IFS}
Suffix hat mich glauben lassen, dass Sie etwas schwierigeres tun, etwa das automatische Wiederherstellen des ursprünglichen IFS am Ende (z. B .: erstes Zeichen wird automatisch verschoben oder so).Der von Ihnen eingegebene Code liefert das gleiche Ergebnis. Versuchen Sie Folgendes, um es besser zu verstehen:
Die Ausgabe sollte jetzt anders sein. Folgendes bekomme ich:
Das hat bei mir geklappt
bash
. Soweit ich weiß, sollte sich ksh nicht wesentlich unterscheiden. Im Wesentlichen wird beim Zitieren$*
alles als ein Wort behandelt, und beim Zitieren$@
wird die Liste als separate Wörter behandelt, wie im obigen Beispiel zu sehen ist.Betrachten Sie dies als Beispiel für die Verwendung der
IFS
Variablen mit$*
Ich bekomme das als Ergebnis:
Außerdem habe ich gerade bestätigt, dass es in der gleichen Weise funktioniert
ksh
. Beidebash
undksh
die hier getesteten waren unter OSX, aber ich kann nicht sehen, wie wichtig das sein würde.quelle
unset IFS
am Ende hinzu, um es auf das Original zurückzusetzen, aber es funktionierte für mich kein Problem undecho $IFS
führte zu der Standardausgabe, die ich davon bekomme. Durch dasIFS
Festlegen der geschweiften Klammern wird ein neuer Bereich eingeführt. Wenn Sie ihn also nicht exportieren, wirkt er sich nicht auf die Außenseite ausIFS
.echo $IFS
beweist gar nichts, denn die Shell sieht das,
, macht dann aber Wortspaltung mitIFS
! Versuchen Sie esecho "$IFS"
.IFS
sollte das lösen.IFS
sei denn, vor dem Aufruf der Funktion wurde ein anderer benutzerdefinierter Wert angegeben. Aber ja, meistens funktioniert das Deaktivieren von IFS.Der Unterschied ist wichtig, wenn Sie Skripte schreiben, die die Positionsparameter richtig verwenden sollen ...
Stellen Sie sich den folgenden Aufruf vor:
Hier gibt es nur 4 Parameter:
In meinem Fall
myuseradd
ist dies nur ein Wrapperuseradd
, der die gleichen Parameter akzeptiert, aber ein Kontingent für den Benutzer hinzufügt:Beachten Sie den Aufruf an
useradd "$@"
, mit$@
zitiert. Dies respektiert die Parameter und sendet sie so, wie sie sinduseradd
. Wenn Sie das Anführungszeichen aufheben$@
(oder$*
auch das Anführungszeichen nicht verwenden), würde useradd 5 Parameter anzeigen , da der dritte Parameter, der ein Leerzeichen enthält, zweigeteilt wird:(und umgekehrt, wenn Sie verwenden würden
"$*"
, würde useradd nur einen Parameter siehe:-m -c Carlos Campderrós ccampderros
)Kurz gesagt, wenn Sie mit Parametern arbeiten müssen, die Mehrwortparameter berücksichtigen, verwenden Sie
"$@"
.quelle
// Mann, schlag . ist ksh, afair, ähnliches Verhalten.
quelle
Über Unterschiede zwischen
zsh
und sprechenbash
:Mit Anführungszeichen
$@
und$*
,zsh
undbash
verhalten sich genauso, und ich denke , das Ergebnis unter allen Schalen ziemlich Standard ist:Ohne Anführungszeichen sind die Ergebnisse für
$*
und gleich$@
, unterscheiden sich jedoch inbash
und inzsh
. In diesem Fallzsh
zeigt sich ein merkwürdiges Verhalten:(Zsh teilt normalerweise keine Textdaten mit IFS, es sei denn, dies wird ausdrücklich angefordert. Beachten Sie jedoch, dass hier das leere Argument unerwartet in der Liste fehlt.)
quelle
$@
ist diesbezüglich nichts Besonderes: Erweitert$x
sich zu höchstens einem Wort, aber leere Variablen werden zu nichts erweitert (kein leeres Wort). Versuchen Sie esprint -l a $foo b
mitfoo
leeren oder undefinierten.Eine der Antworten besagt, dass
$*
(was ich als "Splat" betrachte) selten nützlich ist.Ich suche mit google
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Da URLs oft mit einem geteilt werden
+
, aber meine Tastaturleichter zu erreichen ist als
+
,$*
+$IFS
fühle mich lohnenswert.quelle