it-swarm.com.de

Wann ist eine doppelte Anführungszeichen erforderlich?

Der alte Rat war früher, jeden Ausdruck mit einem $VARIABLE In doppelte Anführungszeichen zu setzen, zumindest wenn man wollte, dass er von der Shell als ein einzelnes Element interpretiert wird, andernfalls alle Leerzeichen im Inhalt von $VARIABLE würde die Shell abwerfen.

Ich verstehe jedoch, dass in neueren Versionen von Shells nicht mehr immer doppelte Anführungszeichen erforderlich sind (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 hingegen sind dieselben drei Befehle erfolgreich. Basierend auf diesem Experiment scheint es daher, dass man in bash die doppelten Anführungszeichen in [[ ... ]], Aber nicht in [ ... ] Noch in Befehlszeilenargumenten weglassen kann, wohingegen In zsh können in all diesen Fällen die doppelten Anführungszeichen weggelassen werden.

Das Ableiten allgemeiner Regeln aus anekdotischen Beispielen wie den oben genannten ist jedoch ein Zufall. 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.

127
kjo

Trennen Sie zuerst zsh vom Rest. Es geht nicht um alte oder moderne Muscheln: zsh verhält sich anders. Die zsh-Designer haben beschlossen, es mit herkömmlichen Shells (Bourne, ksh, bash) nicht kompatibel zu machen, aber einfacher zu bedienen.

Zweitens ist es viel einfacher, ständig doppelte Anführungszeichen zu verwenden, als sich daran zu erinnern, wann sie benötigt werden. Sie werden die meiste Zeit benötigt, daher müssen Sie lernen, wann sie nicht benötigt werden, nicht wann sie benötigt werden.

Kurz gesagt, doppelte Anführungszeichen sind überall dort erforderlich, wo eine Liste von Wörtern oder ein Muster erwartet wird . Sie sind optional in Kontexten, in denen der Parser eine Rohzeichenfolge erwartet.

Was passiert ohne Anführungszeichen?

Beachten Sie, dass ohne doppelte Anführungszeichen zwei Dinge passieren.

  1. Zunächst wird das Ergebnis der Erweiterung (der Wert der Variablen für eine Parametersubstitution wie ${foo} Oder die Ausgabe des Befehls für eine Befehlssubstitution wie $(foo)) überall in Wörter aufgeteilt enthält Leerzeichen.
    Genauer gesagt wird das Ergebnis der Erweiterung bei jedem Zeichen aufgeteilt, das im Wert der Variablen IFS (Trennzeichen) erscheint. Wenn eine Folge von Trennzeichen Leerzeichen enthält (Leerzeichen, Tabulator oder Zeilenumbruch), wird das Leerzeichen als einzelnes Zeichen gezählt. Führende, nachfolgende oder wiederholte Nicht-Leerzeichen-Trennzeichen führen zu leeren Feldern. Zum Beispiel erzeugt IFS=" :" Mit :one::two : three: :four  Leere Felder vor one, zwischen one und two und (einem einzelnen) dazwischen three und four.
  2. Jedes Feld, das sich aus der Aufteilung ergibt, wird als Glob (ein Platzhaltermuster) 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 nicht zitierte Variablenerweiterung $foo Wird umgangssprachlich als "split + glob-Operator" bezeichnet, im Gegensatz zu "$foo", Der nur den Wert der Variablen foo annimmt. Gleiches gilt für die Befehlssubstitution: "$(foo)" ist eine Befehlssubstitution, $(foo) ist eine Befehlssubstitution, 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 der Sie eine Variablen- oder Befehlssubstitution 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, da es sich um ein gewöhnliches eingebautes Schlüsselwort handelt. Dies gilt nur für einige Shells wie Dash, Zsh (in Sh-Emulation), Yash oder Posh. bash und ksh behandeln export beide speziell.

    export VAR="$stuff"
    
  • In einer case Anweisung.

    case $var in …
    

    Beachten Sie, dass Sie in einem Fallmuster doppelte Anführungszeichen benötigen. Die Wortaufteilung findet in einem Fallmuster nicht statt, aber eine nicht zitierte Variable wird als Muster interpretiert, während eine zitierte Variable als Literalzeichenfolge 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-spezielle Syntax.

    [[ -e $filename ]]
    

    Außer dass Sie doppelte Anführungszeichen benötigen, wenn 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 in einfachen Klammern [ … ], Da es sich um eine normale Shell-Syntax handelt (ein Befehl, der zufällig [ Genannt wird). Siehe einfache oder doppelte Klammern

  • Bei einer Umleitung in nicht interaktiven POSIX-Shells (weder bash noch ksh88).

    echo "hello world" >$filename
    

    Einige Shells behandeln den Wert der Variablen, wenn sie interaktiv sind, als Platzhaltermuster. POSIX verbietet dieses Verhalten in nicht interaktiven Shells, aber einige Shells, einschließlich Bash (außer im POSIX-Modus) und ksh88 (einschließlich, wenn es als (angeblich) POSIX sh einiger kommerzieller Unices wie Solaris gefunden wird) tun es immer noch dort (bash versucht auch Teilen und die Umleitung schlägt fehl, es sei denn, dass Teilen + Globben genau ein Wort ergibt), weshalb es ist Es ist besser, Umleitungsziele in einem sh - Skript anzugeben, falls Sie es eines Tages in ein bash - Skript konvertieren möchten, oder es auf einem System auszuführen, auf dem sh nicht in diesem Punkt konform, oder es kann bezogen von interaktiven Shells sein.

  • Innerhalb eines arithmetischen Ausdrucks. Tatsächlich müssen Sie die Anführungszeichen weglassen, damit eine Variable als arithmetischer Ausdruck analysiert werden kann.

    expr=2*2
    echo "$(($expr))"
    

    Sie benötigen jedoch die Anführungszeichen um die arithmetische Erweiterung, da sie in den meisten Shells der Word-Aufteilung unterliegen, wie es POSIX erfordert (!?).

  • In einem assoziativen Array-Index.

    typeset -A a
    i='foo bar*qux'
    a[foo\ bar\*qux]=hello
    echo "${a[$i]}"
    

Eine nicht zitierte Variablen- und Befehlssubstitution kann 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, wurde $IFS Nicht geändert und Sie möchten ihn in Leerzeichen aufteilen.
  • Wenn Sie einen Wert für ein bestimmtes Zeichen teilen möchten: Deaktivieren Sie das Globbing mit set -f, Setzen Sie IFS auf das Trennzeichen (oder lassen Sie es in Leerzeichen teilen) und führen Sie dann die Erweiterung durch.

Zsh

In zsh können Sie die doppelten Anführungszeichen mit wenigen Ausnahmen meistens weglassen.

  • $var Wird niemals auf mehrere Wörter erweitert, jedoch auf die leere Liste (im Gegensatz zu einer Liste mit einem einzelnen, leeren Wort), wenn der Wert von var die leere Zeichenfolge ist. Kontrast:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo
    

    In ähnlicher Weise wird "${array[@]}" Auf alle Elemente des Arrays erweitert, während $array Nur auf die nicht leeren Elemente erweitert wird.

  • Das Parametererweiterungsflag @ Erfordert manchmal doppelte Anführungszeichen um die gesamte Ersetzung: "${(@)foo}".

  • Die Befehlssubstitution wird ohne Angabe von Feldern aufgeteilt: echo $(echo 'a'; echo '*') druckt a * (Mit einem Leerzeichen), während echo "$(echo 'a'; echo '*')" die unveränderte zweizeilige Zeichenfolge druckt. Verwenden Sie "$(somecommand)", um die Ausgabe des Befehls in einem einzigen Wort ohne letzte Zeilenumbrüche abzurufen. Verwenden Sie "${$(somecommand; echo _)%?}", um die genaue Ausgabe des Befehls einschließlich der letzten Zeilenumbrüche zu erhalten. Verwenden Sie "${(@f)$(somecommand)}", um ein Array von Zeilen aus der Befehlsausgabe abzurufen.