Der alte Rat war früher, jeden Ausdruck mit $VARIABLE
in doppelte Anführungszeichen zu setzen , zumindest wenn man wollte, dass es von der Shell als ein einzelnes Element interpretiert wird, ansonsten irgendwelche Leerzeichen im Inhalt von $VARIABLE
würde die Schale abwerfen.
Ich verstehe jedoch, dass in neueren Versionen von Shells doppelte Anführungszeichen nicht mehr immer benötigt werden (zumindest für den oben beschriebenen Zweck). Zum Beispiel in bash
:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
In zsh
, andererseits sind dieselben drei Befehle erfolgreich. Daher scheint es, basierend auf diesem Experiment, in bash
, kann man die doppelten Anführungszeichen innerhalb von [[ ... ]]
weglassen , aber nicht in [ ... ]
noch in Befehlszeilenargumenten, wohingegen in zsh
, die doppelten Anführungszeichen können in all diesen Fällen weggelassen werden.
Aber allgemeine Regeln aus anekdotischen Beispielen wie dem obigen abzuleiten, ist ein riskantes Unterfangen. Es wäre schön, eine Zusammenfassung zu sehen, wann doppelte Anführungszeichen erforderlich sind. Ich interessiere mich hauptsächlich für zsh
, bash
, und /bin/sh
.
Akzeptierte Antwort:
Trennen Sie zuerst zsh vom Rest. Es geht nicht um alte oder moderne Shells:zsh verhält sich anders. Die zsh-Designer entschieden sich dafür, es mit traditionellen Shells (Bourne, ksh, bash) inkompatibel, aber einfacher zu verwenden.
Zweitens ist es viel einfacher, die ganze Zeit doppelte Anführungszeichen zu verwenden, als sich daran zu erinnern, wann sie benötigt werden. Sie werden die meiste Zeit benötigt, also müssen Sie lernen, wann sie nicht benötigt werden, und nicht, wenn sie benötigt werden.
Kurz gesagt, doppelte Anführungszeichen sind überall dort notwendig, wo eine Liste von Wörtern oder ein Muster erwartet wird . Sie sind optional in Kontexten, in denen der Parser einen Rohstring erwartet.
Was passiert ohne Anführungszeichen
Beachten Sie, dass ohne doppelte Anführungszeichen zwei Dinge passieren.
- Zunächst das Ergebnis der Erweiterung (der Wert der Variablen für eine Parameterersetzung wie
${foo}
, oder die Ausgabe des Befehls für eine Befehlsersetzung wie$(foo)
) wird überall dort in Wörter zerlegt, wo es Leerzeichen enthält.
Genauer gesagt wird das Ergebnis der Erweiterung an jedem Zeichen zerlegt, das im Wert desIFS
vorkommt Variable (Trennzeichen). Wenn eine Folge von Trennzeichen Leerzeichen (Leerzeichen, Tabulator oder Zeilenumbruch) enthält, wird das Leerzeichen als einzelnes Zeichen gezählt; Führende, nachgestellte oder wiederholte Nicht-Leerzeichen-Trennzeichen führen zu leeren Feldern. Zum Beispiel mitIFS=" :"
,:one::two : three: :four
erzeugt leere Felder vorone
, zwischenone
undtwo
, und (eine einzelne) zwischenthree
undfour
. - Jedes Feld, das aus der Aufteilung resultiert, wird als Glob (ein Wildcard-Muster) interpretiert, wenn es eines der Zeichen
[*?
enthält . Wenn dieses Muster mit einem oder mehreren Dateinamen übereinstimmt, wird das Muster durch die Liste der übereinstimmenden Dateinamen ersetzt.
Eine Variablenerweiterung ohne Anführungszeichen $foo
wird umgangssprachlich als „Split+Glob-Operator“ bezeichnet, im Gegensatz zu "$foo"
die nur den Wert der Variablen foo
nimmt . Dasselbe gilt für die Befehlsersetzung:"$(foo)"
ist eine Befehlsersetzung, $(foo)
ist eine Befehlsersetzung gefolgt von split+glob.
Wo Sie die doppelten Anführungszeichen weglassen können
Hier sind alle Fälle, die mir in einer Shell im Bourne-Stil einfallen, in denen Sie eine Variablen- oder Befehlsersetzung ohne doppelte Anführungszeichen schreiben können und der Wert wörtlich interpretiert wird.
-
Auf der rechten Seite einer Aufgabe.
var=$stuff a_single_star=*
Beachten Sie, dass Sie die doppelten Anführungszeichen nach
export
benötigen , weil es ein gewöhnliches Builtin ist, kein Schlüsselwort. Dies gilt nur für einige Shells wie dash, zsh (in der Sh-Emulation), yash oder posh; bash und ksh behandeln beideexport
speziell.export VAR="$stuff"
-
In einem
case
Aussage.case $var in …
Beachten Sie, dass Sie in einem Case-Muster doppelte Anführungszeichen benötigen. In einem Fallmuster findet keine Wortaufteilung statt, aber eine Variable ohne Anführungszeichen wird als Muster interpretiert, während eine Variable in Anführungszeichen als Literal-String interpretiert wird.
a_star='a*' case $var in "$a_star") echo "'$var' is the two characters a, *";; $a_star) echo "'$var' begins with a";; esac
-
In doppelten Klammern. Doppelte Klammern sind Shell-spezifische Syntax.
[[ -e $filename ]]
Außer dass Sie doppelte Anführungszeichen benötigen, wo ein Muster oder ein regulärer Ausdruck erwartet wird:auf der rechten Seite von
=
oder==
oder!=
oder=~
.a_star='a*' if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *" elif [[ $var == $a_star ]]; then echo "'$var' begins with a" fi
Sie benötigen wie üblich doppelte Anführungszeichen innerhalb einfacher Klammern
[ … ]
weil es sich um gewöhnliche Shell-Syntax handelt (es ist ein Befehl, der zufällig[
heißt ). Siehe Einfache oder doppelte Klammern -
Bei einer Umleitung in nicht interaktiven POSIX-Shells (nicht
bash
, nochksh88
).echo "hello world" >$filename
Einige Shells behandeln, wenn sie interaktiv sind, den Wert der Variablen als Platzhaltermuster. POSIX verbietet dieses Verhalten in nicht-interaktiven Shells, aber ein paar Shells, einschließlich bash (außer im POSIX-Modus) und ksh88 (einschließlich, wenn sie als (angeblich) POSIX
sh
gefunden werden einiger kommerzieller Unices wie Solaris) machen es dort immer noch (bash
versucht auch aufzuteilen und die Umleitung schlägt fehl, es sei denn, split+globbing ergibt genau ein Wort), weshalb es besser ist, Ziele von Weiterleitungen in einemsh
anzugeben Skript, falls Sie es in einebash
umwandeln möchten Skript eines Tages, oder führen Sie es auf einem System aus, auf demsh
ist in diesem Punkt nicht konform, oder es kann bezogen sein von interaktiven Shells. -
Innerhalb eines arithmetischen Ausdrucks. Tatsächlich müssen Sie die Anführungszeichen weglassen, damit eine Variable als arithmetischer Ausdruck geparst wird.
expr=2*2 echo "$(($expr))"
Sie benötigen jedoch die Anführungszeichen um die arithmetische Erweiterung, da sie in den meisten Shells der Wortaufteilung unterliegen, wie es POSIX erfordert (!?).
-
In einem assoziativen Array tiefgestellt.
typeset -A a i='foo bar*qux' a[foo bar*qux]=hello echo "${a[$i]}"
Eine Variable ohne Anführungszeichen und eine Befehlsersetzung können in einigen seltenen Fällen nützlich sein:
- Wenn der Variablenwert oder die Befehlsausgabe aus einer Liste von Glob-Mustern besteht und Sie diese Muster auf die Liste der übereinstimmenden Dateien erweitern möchten.
- Wenn Sie wissen, dass der Wert kein Platzhalterzeichen enthält, wird dieser
$IFS
wurde nicht geändert und Sie möchten es an Leerzeichen trennen. - Wenn Sie einen Wert an einem bestimmten Zeichen aufteilen möchten:Deaktivieren Sie das Globbing mit
set -f
, setzen SieIFS
auf das Trennzeichen (oder lassen Sie es in Ruhe, um es bei Leerzeichen zu teilen), und führen Sie dann die Erweiterung durch.
Schsch
In zsh können Sie die doppelten Anführungszeichen meistens weglassen, mit wenigen Ausnahmen.
-
$var
wird niemals auf mehrere Wörter erweitert, jedoch auf die leere Liste (im Gegensatz zu einer Liste, die ein einzelnes, leeres Wort enthält), wenn der Wert vonvar
ist die leere Zeichenfolge. Kontrast:var= print -l $var foo # prints just foo print -l "$var" foo # prints an empty line, then foo
Ebenso
"${array[@]}"
erweitert sich auf alle Elemente des Arrays, während$array
expandiert nur auf die nicht leeren Elemente. -
Der
@
Parametererweiterungs-Flag erfordert manchmal doppelte Anführungszeichen um die gesamte Ersetzung:"${(@)foo}"
. -
Die Befehlsersetzung wird einer Feldaufteilung unterzogen, wenn sie nicht in Anführungszeichen steht:
echo $(echo 'a'; echo '*')
gibta *
aus (mit einem einzelnen Leerzeichen), wohingegenecho "$(echo 'a'; echo '*')"
gibt den unveränderten zweizeiligen String aus. Verwenden Sie"$(somecommand)"
um die Ausgabe des Befehls in einem einzigen Wort ohne abschließende Zeilenumbrüche zu erhalten. Verwenden Sie"${$(somecommand; echo _)%?}"
um die genaue Ausgabe des Befehls einschließlich abschließender Zeilenumbrüche zu erhalten. Verwenden Sie"${(@f)$(somecommand)}"
um ein Array von Zeilen aus der Ausgabe des Befehls zu erhalten.