Diese Frage enthält viele Duplikate, von denen sich viele nicht mit Variablen befassen. Daher habe ich den Titel "Wert" anstelle von "Variable" verwendet. Ich hoffe, dies hilft mehr Menschen, dieses Thema zu finden.
Tripleee
1
@codeforester Was ist mit der rückgängig gemachten Bearbeitung los?
Allgemeine Regel: Zitieren Sie es, wenn es entweder leer sein kann oder Leerzeichen (oder wirklich Leerzeichen) oder Sonderzeichen (Platzhalter) enthalten kann. Das Nicht-Zitieren von Zeichenfolgen mit Leerzeichen führt häufig dazu, dass die Shell ein einzelnes Argument in viele zerlegt.
$?benötigt keine Anführungszeichen, da es sich um einen numerischen Wert handelt. Ob es $URLbenötigt wird, hängt davon ab, was Sie dort zulassen und ob Sie noch ein Argument wünschen, wenn es leer ist.
Ich neige dazu, Zeichenfolgen immer nur aus Gewohnheit zu zitieren, da dies sicherer ist.
Beachten Sie, dass "Leerzeichen" wirklich "Leerzeichen" bedeuten.
William Pursell
4
@Cristian: Wenn Sie nicht sicher sind, was in der Variablen enthalten sein könnte, ist es sicherer, sie zu zitieren. Ich neige dazu, dem gleichen Prinzip wie paxdiablo zu folgen und es mir zur Gewohnheit zu machen, alles zu zitieren (es sei denn, es gibt einen bestimmten Grund, dies nicht zu tun).
Gordon Davisson
11
Wenn Sie den Wert von IFS nicht kennen, zitieren Sie ihn, egal was passiert. Wenn ja IFS=0, echo $?kann das sehr überraschend sein.
Charles Duffy
3
Zitat basierend auf dem Kontext, nicht basierend auf dem, was Sie von den Werten erwarten, sonst werden Ihre Fehler schlimmer. Zum Beispiel, sind Sie sicher , dass keine Ihrer Pfade Räume haben, so dass Sie denken Sie schreiben können cp $source1 $source2 $dest, aber wenn für einige unerwarteten Grund destnicht gesetzt bekommt, nur das dritte Argument verschwindet, und es wird still kopieren source1über source2Sie stattdessen eine des Gebens entsprechender Fehler für das leere Ziel (wie es der Fall gewesen wäre, wenn Sie jedes Argument zitiert hätten).
Derek Veit
3
quote it if...hat den Denkprozess rückwärts - Anführungszeichen fügen Sie nicht hinzu, wenn Sie sie benötigen, sondern entfernen sie, wenn Sie sie benötigen. Immer Strings und Skripte in einfache Anführungszeichen wickeln , es sei denn Sie müssen doppelte Anführungszeichen verwenden (zB lassen eine Variable erweitern) oder Notwendigkeit keine Anführungszeichen zu verwenden (zB zu tun Globbing und Dateinamen Expansion).
Ed Morton
90
Kurz gesagt, zitieren Sie alles, wo Sie die Shell nicht benötigen, um Token-Splitting und Wildcard-Erweiterung durchzuführen.
Einfache Anführungszeichen schützen den Text zwischen ihnen wörtlich. Es ist das richtige Werkzeug, wenn Sie sicherstellen müssen, dass die Shell die Saite überhaupt nicht berührt. In der Regel ist dies der Anführungsmechanismus der Wahl, wenn Sie keine variable Interpolation benötigen.
$ echo 'Nothing \t in here $will change'Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Doppelte Anführungszeichen sind geeignet, wenn eine variable Interpolation erforderlich ist. Mit geeigneten Anpassungen ist es auch eine gute Problemumgehung, wenn Sie einfache Anführungszeichen in der Zeichenfolge benötigen. (Es gibt keine einfache Möglichkeit, ein einfaches Anführungszeichen zwischen einfachen Anführungszeichen zu umgehen, da es in einfachen Anführungszeichen keinen Escape-Mechanismus gibt. Wenn dies der Fall wäre, würden sie nicht vollständig wörtlich zitieren.)
$ echo "There is no place like '$HOME'"There is no place like '/home/me'
Keine Anführungszeichen sind geeignet, wenn die Shell speziell eine Token-Aufteilung und / oder Wildcard-Erweiterung durchführen soll.
Token-Aufteilung;
$ words="foo bar baz"
$ for word in $words;do> echo "$word">done
foo
bar
baz
Im Gegensatz:
$ for word in"$words";do echo "$word";done
foo bar baz
(Die Schleife wird nur einmal über die einzelne Zeichenfolge in Anführungszeichen ausgeführt.)
$ for word in'$words';do echo "$word";done
$words
(Die Schleife wird nur einmal über die wörtliche Zeichenfolge in einfachen Anführungszeichen ausgeführt.)
Platzhaltererweiterung:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
Im Gegensatz:
$ ls "$pattern"
ls: cannot access file*.txt:No such file or directory
(Es gibt keine wörtlich benannte Datei file*.txt.)
$ ls '$pattern'
ls: cannot access $pattern:No such file or directory
(Es gibt auch keine Datei mit dem Namen $pattern!)
Konkreter sollte alles, was einen Dateinamen enthält, normalerweise in Anführungszeichen gesetzt werden (da Dateinamen Leerzeichen und andere Shell-Metazeichen enthalten können). Alles, was eine URL enthält, sollte normalerweise in Anführungszeichen gesetzt werden (da viele URLs Shell-Metazeichen wie ?und enthalten &). Alles, was einen regulären Ausdruck enthält, sollte normalerweise zitiert werden (dito dito). Alles, was signifikante Leerzeichen außer einzelnen Leerzeichen zwischen Nicht-Leerzeichen enthält, muss in Anführungszeichen gesetzt werden (da die Shell sonst das Leerzeichen effektiv in einzelne Leerzeichen einfügt und alle führenden oder nachfolgenden Leerzeichen abschneidet).
Wenn Sie wissen, dass eine Variable nur einen Wert enthalten kann, der keine Shell-Metazeichen enthält, ist das Zitieren optional. Ein nicht zitiertes Element $?ist also grundsätzlich in Ordnung, da diese Variable immer nur eine einzige Zahl enthalten kann. Es "$?"ist jedoch auch korrekt und wird aus Gründen der allgemeinen Konsistenz und Korrektheit empfohlen (obwohl dies meine persönliche Empfehlung ist, keine allgemein anerkannte Richtlinie).
Werte, die keine Variablen sind, folgen grundsätzlich denselben Regeln, obwohl Sie dann auch alle Metazeichen umgehen können, anstatt sie in Anführungszeichen zu setzen. In einem allgemeinen Beispiel wird eine URL mit einem &darin von der Shell als Hintergrundbefehl analysiert, es sei denn, das Metazeichen wird maskiert oder in Anführungszeichen gesetzt:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Dies geschieht natürlich auch, wenn sich die URL in einer Variablen ohne Anführungszeichen befindet.) Für eine statische Zeichenfolge sind einfache Anführungszeichen am sinnvollsten, obwohl hier jede Form von Anführungszeichen oder Escapezeichen funktioniert.
wget 'http://example.com/q&uack'# Single quotes preferred for a static string
wget "http://example.com/q&uack"# Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
Das letzte Beispiel schlägt auch ein anderes nützliches Konzept vor, das ich gerne als "Wippzitat" bezeichne. Wenn Sie einfache und doppelte Anführungszeichen mischen müssen, können Sie sie nebeneinander verwenden. Zum Beispiel die folgenden Zeichenfolgen in Anführungszeichen
'$HOME '"isn't"' where `<3'"' is."
kann hintereinander eingefügt werden und bildet nach dem Tokenisieren und Entfernen von Anführungszeichen eine einzige lange Zeichenfolge.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Dies ist nicht schlecht lesbar, aber es ist eine übliche Technik und daher gut zu wissen.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern;do# definitely, definitely not ``for file in $(ls $pattern)''> printf 'Found file: %s\n'"$file">doneFound file: file1.txt
Found file: file_other.txt
(Die Schleife ist im letzteren Beispiel völlig überflüssig. Sie printffunktioniert auch mit mehreren Argumenten stateinwandfrei. Das Schleifen über eine Platzhalterübereinstimmung ist jedoch ein häufiges Problem und wird häufig falsch ausgeführt.)
Eine Variable, die eine Liste von Token enthält, die durchlaufen werden sollen, oder ein Platzhalter, der erweitert werden soll, wird seltener angezeigt. Daher wird manchmal die Abkürzung "Alles zitieren, wenn Sie nicht genau wissen, was Sie tun" abgekürzt.
Dies ist eine Variante (eines Teils) einer Antwort, die ich auf eine verwandte Frage gepostet habe . Ich füge es hier ein, weil dies prägnant und klar genug ist, um eine kanonische Frage für dieses spezielle Problem zu werden.
Tripleee
4
Ich werde bemerken, dass dies Punkt 0 und ein wiederkehrendes Thema in der mywiki.wooledge.org/BashPitfalls-Sammlung häufiger Bash-Fehler ist. Viele, viele der einzelnen Elemente auf dieser Liste befassen sich im Wesentlichen mit diesem Problem.
Tripleee
26
Hier ist eine Drei-Punkte-Formel für Anführungszeichen im Allgemeinen:
Anführungszeichen
In Kontexten, in denen wir das Teilen und Globbing von Wörtern unterdrücken möchten. Auch in Kontexten, in denen das Literal als Zeichenfolge und nicht als regulärer Ausdruck behandelt werden soll.
Einzelzitate
In String-Literalen, in denen die Interpolation und die spezielle Behandlung von Backslashes unterdrückt werden sollen. Mit anderen Worten, Situationen, in denen die Verwendung von doppelten Anführungszeichen unangemessen wäre.
Keine Zitate
In Kontexten, in denen wir absolut sicher sind, dass es keine Probleme beim Aufteilen oder Globbing von Wörtern gibt, oder wenn wir das Aufteilen und Globbing von Wörtern wünschen .
Beispiele
Anführungszeichen
Literalzeichenfolgen mit Leerzeichen ( "StackOverflow rocks!", "Steve's Apple")
variable Erweiterungen ( "$var", "${arr[@]}")
Befehlsersetzungen ( "$(ls)", "`ls`")
Globs, in denen der Verzeichnispfad oder der Dateinamenbereich Leerzeichen enthält ( "/my dir/"*)
um einfache Anführungszeichen zu schützen ( "single'quote'delimited'string")
Bash-Parametererweiterung ( "${filename##*/}")
Einzelzitate
Befehlsnamen und Argumente mit Leerzeichen
Literalzeichenfolgen, deren Interpolation unterdrückt werden muss ( 'Really costs $$!', 'just a backslash followed by a t: \t')
um doppelte Anführungszeichen zu schützen ( 'The "crux"')
Regex-Literale, deren Interpolation unterdrückt werden muss
Verwenden Sie Shell-Anführungszeichen für Literale mit Sonderzeichen ( $'\n\t')
Verwenden Sie Shell-Anführungszeichen, wenn Sie mehrere einfache und doppelte Anführungszeichen schützen müssen ( $'{"table": "users", "where": "first_name"=\'Steve\'}')
Keine Zitate
um Standard - numerische Variablen ( $$, $?, $#etc.)
in arithmetischen Kontexten wie ((count++)), "${arr[idx]}","${string:start:length}"
Insider- [[ ]]Ausdruck, der frei von Problemen bei der Wortaufteilung und beim Globbing ist (dies ist eine Frage des Stils und die Meinungen können stark variieren).
wo wir Wortaufteilung wollen ( for word in $words)
wo wir globbing wollen ( for txtfile in *.txt; do ...)
wo wir ~interpretiert werden wollen als $HOME( ~/"some dir"aber nicht "~/some dir")
Nach diesen Richtlinien würde man eine Liste der Dateien im Stammverzeichnis erhalten, indem man schreibt. "ls" "/" Der Ausdruck "alle Zeichenfolgenkontexte" muss sorgfältiger qualifiziert werden.
William Pursell
5
In [[ ]]ist das Zitieren auf der rechten Seite von =/ ==und wichtig =~: Es macht den Unterschied zwischen der Interpretation einer Zeichenfolge als Muster / Regex oder wörtlich.
Benjamin W.
6
Ein guter Überblick, aber die Kommentare von @ BenjaminW. sind eine Integration wert und ANSI C-zitierte Strings ( $'...') sollten definitiv einen eigenen Abschnitt haben.
mklement0
3
@ mklement0, in der Tat sind sie gleichwertig. Diese Richtlinien weisen darauf hin, dass Sie immer "ls" "/"anstelle der allgemeineren tippen sollten ls /, und ich betrachte dies als einen Hauptfehler in den Richtlinien.
William Pursell
4
Für keine Anführungszeichen können Sie Variablenzuweisung hinzufügen oder case:)
PesaThe
4
Ich benutze in der Regel zitiert wie "$var"für sicher, es sei denn, ich bin sicher, dass $vardas keinen Platz enthält.
Ich benutze $varals einfache Möglichkeit, Linien zu verbinden:
lines="`cat multi-lines-text-file.txt`"
echo "$lines"## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Der letzte Kommentar ist etwas irreführend; Die Zeilenumbrüche werden effektiv durch Leerzeichen ersetzt und nicht einfach entfernt.
Tripleee
-1
Verwenden Sie für die Verwendung der Variablen im Shell-Skript "" in Anführungszeichen, da die Variable Leerzeichen oder Sonderzeichen enthält, die die Ausführung Ihres Shell-Skripts nicht beeinflussen. Andernfalls können Sie sie ohne "" verwenden, wenn Sie sicher sind, dass Ihr Variablenname keine Leerzeichen oder Sonderzeichen enthält.
Beispiel:
echo "$ url name" - (Kann jederzeit verwendet werden)
echo "$ url name" - (Kann in solchen Situationen nicht verwendet werden, seien Sie also vorsichtig, bevor Sie es verwenden)
Antworten:
Allgemeine Regel: Zitieren Sie es, wenn es entweder leer sein kann oder Leerzeichen (oder wirklich Leerzeichen) oder Sonderzeichen (Platzhalter) enthalten kann. Das Nicht-Zitieren von Zeichenfolgen mit Leerzeichen führt häufig dazu, dass die Shell ein einzelnes Argument in viele zerlegt.
$?
benötigt keine Anführungszeichen, da es sich um einen numerischen Wert handelt. Ob es$URL
benötigt wird, hängt davon ab, was Sie dort zulassen und ob Sie noch ein Argument wünschen, wenn es leer ist.Ich neige dazu, Zeichenfolgen immer nur aus Gewohnheit zu zitieren, da dies sicherer ist.
quelle
IFS=0
,echo $?
kann das sehr überraschend sein.cp $source1 $source2 $dest
, aber wenn für einige unerwarteten Grunddest
nicht gesetzt bekommt, nur das dritte Argument verschwindet, und es wird still kopierensource1
übersource2
Sie stattdessen eine des Gebens entsprechender Fehler für das leere Ziel (wie es der Fall gewesen wäre, wenn Sie jedes Argument zitiert hätten).quote it if...
hat den Denkprozess rückwärts - Anführungszeichen fügen Sie nicht hinzu, wenn Sie sie benötigen, sondern entfernen sie, wenn Sie sie benötigen. Immer Strings und Skripte in einfache Anführungszeichen wickeln , es sei denn Sie müssen doppelte Anführungszeichen verwenden (zB lassen eine Variable erweitern) oder Notwendigkeit keine Anführungszeichen zu verwenden (zB zu tun Globbing und Dateinamen Expansion).Kurz gesagt, zitieren Sie alles, wo Sie die Shell nicht benötigen, um Token-Splitting und Wildcard-Erweiterung durchzuführen.
Einfache Anführungszeichen schützen den Text zwischen ihnen wörtlich. Es ist das richtige Werkzeug, wenn Sie sicherstellen müssen, dass die Shell die Saite überhaupt nicht berührt. In der Regel ist dies der Anführungsmechanismus der Wahl, wenn Sie keine variable Interpolation benötigen.
Doppelte Anführungszeichen sind geeignet, wenn eine variable Interpolation erforderlich ist. Mit geeigneten Anpassungen ist es auch eine gute Problemumgehung, wenn Sie einfache Anführungszeichen in der Zeichenfolge benötigen. (Es gibt keine einfache Möglichkeit, ein einfaches Anführungszeichen zwischen einfachen Anführungszeichen zu umgehen, da es in einfachen Anführungszeichen keinen Escape-Mechanismus gibt. Wenn dies der Fall wäre, würden sie nicht vollständig wörtlich zitieren.)
Keine Anführungszeichen sind geeignet, wenn die Shell speziell eine Token-Aufteilung und / oder Wildcard-Erweiterung durchführen soll.
Token-Aufteilung;
Im Gegensatz:
(Die Schleife wird nur einmal über die einzelne Zeichenfolge in Anführungszeichen ausgeführt.)
(Die Schleife wird nur einmal über die wörtliche Zeichenfolge in einfachen Anführungszeichen ausgeführt.)
Platzhaltererweiterung:
Im Gegensatz:
(Es gibt keine wörtlich benannte Datei
file*.txt
.)(Es gibt auch keine Datei mit dem Namen
$pattern
!)Konkreter sollte alles, was einen Dateinamen enthält, normalerweise in Anführungszeichen gesetzt werden (da Dateinamen Leerzeichen und andere Shell-Metazeichen enthalten können). Alles, was eine URL enthält, sollte normalerweise in Anführungszeichen gesetzt werden (da viele URLs Shell-Metazeichen wie
?
und enthalten&
). Alles, was einen regulären Ausdruck enthält, sollte normalerweise zitiert werden (dito dito). Alles, was signifikante Leerzeichen außer einzelnen Leerzeichen zwischen Nicht-Leerzeichen enthält, muss in Anführungszeichen gesetzt werden (da die Shell sonst das Leerzeichen effektiv in einzelne Leerzeichen einfügt und alle führenden oder nachfolgenden Leerzeichen abschneidet).Wenn Sie wissen, dass eine Variable nur einen Wert enthalten kann, der keine Shell-Metazeichen enthält, ist das Zitieren optional. Ein nicht zitiertes Element
$?
ist also grundsätzlich in Ordnung, da diese Variable immer nur eine einzige Zahl enthalten kann. Es"$?"
ist jedoch auch korrekt und wird aus Gründen der allgemeinen Konsistenz und Korrektheit empfohlen (obwohl dies meine persönliche Empfehlung ist, keine allgemein anerkannte Richtlinie).Werte, die keine Variablen sind, folgen grundsätzlich denselben Regeln, obwohl Sie dann auch alle Metazeichen umgehen können, anstatt sie in Anführungszeichen zu setzen. In einem allgemeinen Beispiel wird eine URL mit einem
&
darin von der Shell als Hintergrundbefehl analysiert, es sei denn, das Metazeichen wird maskiert oder in Anführungszeichen gesetzt:(Dies geschieht natürlich auch, wenn sich die URL in einer Variablen ohne Anführungszeichen befindet.) Für eine statische Zeichenfolge sind einfache Anführungszeichen am sinnvollsten, obwohl hier jede Form von Anführungszeichen oder Escapezeichen funktioniert.
Das letzte Beispiel schlägt auch ein anderes nützliches Konzept vor, das ich gerne als "Wippzitat" bezeichne. Wenn Sie einfache und doppelte Anführungszeichen mischen müssen, können Sie sie nebeneinander verwenden. Zum Beispiel die folgenden Zeichenfolgen in Anführungszeichen
kann hintereinander eingefügt werden und bildet nach dem Tokenisieren und Entfernen von Anführungszeichen eine einzige lange Zeichenfolge.
Dies ist nicht schlecht lesbar, aber es ist eine übliche Technik und daher gut zu wissen.
Abgesehen davon sollten Skripte normalerweise
ls
für nichts verwendet werden. Um einen Platzhalter zu erweitern, verwenden Sie ihn einfach.(Die Schleife ist im letzteren Beispiel völlig überflüssig. Sie
printf
funktioniert auch mit mehreren Argumentenstat
einwandfrei. Das Schleifen über eine Platzhalterübereinstimmung ist jedoch ein häufiges Problem und wird häufig falsch ausgeführt.)Eine Variable, die eine Liste von Token enthält, die durchlaufen werden sollen, oder ein Platzhalter, der erweitert werden soll, wird seltener angezeigt. Daher wird manchmal die Abkürzung "Alles zitieren, wenn Sie nicht genau wissen, was Sie tun" abgekürzt.
quelle
Hier ist eine Drei-Punkte-Formel für Anführungszeichen im Allgemeinen:
Anführungszeichen
In Kontexten, in denen wir das Teilen und Globbing von Wörtern unterdrücken möchten. Auch in Kontexten, in denen das Literal als Zeichenfolge und nicht als regulärer Ausdruck behandelt werden soll.
Einzelzitate
In String-Literalen, in denen die Interpolation und die spezielle Behandlung von Backslashes unterdrückt werden sollen. Mit anderen Worten, Situationen, in denen die Verwendung von doppelten Anführungszeichen unangemessen wäre.
Keine Zitate
In Kontexten, in denen wir absolut sicher sind, dass es keine Probleme beim Aufteilen oder Globbing von Wörtern gibt, oder wenn wir das Aufteilen und Globbing von Wörtern wünschen .
Beispiele
Anführungszeichen
"StackOverflow rocks!"
,"Steve's Apple"
)"$var"
,"${arr[@]}"
)"$(ls)"
,"`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Einzelzitate
'Really costs $$!'
,'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Keine Zitate
$$
,$?
,$#
etc.)((count++))
,"${arr[idx]}"
,"${string:start:length}"
[[ ]]
Ausdruck, der frei von Problemen bei der Wortaufteilung und beim Globbing ist (dies ist eine Frage des Stils und die Meinungen können stark variieren).for word in $words
)for txtfile in *.txt; do ...
)~
interpretiert werden wollen als$HOME
(~/"some dir"
aber nicht"~/some dir"
)Siehe auch:
quelle
"ls" "/"
Der Ausdruck "alle Zeichenfolgenkontexte" muss sorgfältiger qualifiziert werden.[[ ]]
ist das Zitieren auf der rechten Seite von=
/==
und wichtig=~
: Es macht den Unterschied zwischen der Interpretation einer Zeichenfolge als Muster / Regex oder wörtlich.$'...'
) sollten definitiv einen eigenen Abschnitt haben."ls" "/"
anstelle der allgemeineren tippen solltenls /
, und ich betrachte dies als einen Hauptfehler in den Richtlinien.case
:)Ich benutze in der Regel zitiert wie
"$var"
für sicher, es sei denn, ich bin sicher, dass$var
das keinen Platz enthält.Ich benutze
$var
als einfache Möglichkeit, Linien zu verbinden:quelle
Verwenden Sie für die Verwendung der Variablen im Shell-Skript "" in Anführungszeichen, da die Variable Leerzeichen oder Sonderzeichen enthält, die die Ausführung Ihres Shell-Skripts nicht beeinflussen. Andernfalls können Sie sie ohne "" verwenden, wenn Sie sicher sind, dass Ihr Variablenname keine Leerzeichen oder Sonderzeichen enthält.
Beispiel:
echo "$ url name" - (Kann jederzeit verwendet werden)
echo "$ url name" - (Kann in solchen Situationen nicht verwendet werden, seien Sie also vorsichtig, bevor Sie es verwenden)
quelle