Könnte mir jemand sagen, ob ich Variablen in einem Shell-Skript in Anführungszeichen setzen soll oder nicht?
Ist beispielsweise Folgendes richtig:
xdg-open $URL
[ $? -eq 2 ]
oder
xdg-open "$URL"
[ "$?" -eq "2" ]
Und wenn ja, warum?
Könnte mir jemand sagen, ob ich Variablen in einem Shell-Skript in Anführungszeichen setzen soll oder nicht?
Ist beispielsweise Folgendes richtig:
xdg-open $URL
[ $? -eq 2 ]
oder
xdg-open "$URL"
[ "$?" -eq "2" ]
Und wenn ja, warum?
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.
IFS=0
, echo $?
kann das sehr überraschend sein.
cp $source1 $source2 $dest
, aber wenn für einige unerwarteten Grund dest
nicht gesetzt bekommt, nur das dritte Argument verschwindet, und es wird still kopieren source1
über source2
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.
$ 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.
Abgesehen davon sollten Skripte normalerweise ls
für nichts verwendet werden. Um einen Platzhalter zu erweitern, verwenden Sie ihn einfach.
$ 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"
> done
Found file: file1.txt
Found file: file_other.txt
(Die Schleife ist im letzteren Beispiel völlig überflüssig. Sie printf
funktioniert auch mit mehreren Argumenten stat
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.
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:
"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 sollten ls /
, 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:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
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)