GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wann sind doppelte Anführungszeichen erforderlich?

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.

  1. 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 des IFS 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 mit IFS=" :" , :one::two : three: :four  erzeugt leere Felder vor one , zwischen one und two , und (eine einzelne) zwischen three und four .
  2. 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 beide export 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 , noch ksh88 ).

    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 einem sh anzugeben Skript, falls Sie es in eine bash umwandeln möchten Skript eines Tages, oder führen Sie es auf einem System aus, auf dem sh 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 Sie IFS auf das Trennzeichen (oder lassen Sie es in Ruhe, um es bei Leerzeichen zu teilen), und führen Sie dann die Erweiterung durch.
Verwandte:Warum Swap verwenden, wenn im RAM mehr als genug freier Speicherplatz vorhanden ist?

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 von var 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 '*') gibt a * aus (mit einem einzelnen Leerzeichen), wohingegen echo "$(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.

Verwandte:typeset -A gibt Fehler im Skript?
Linux
  1. Wie entkomme ich Zitaten in der Shell?

  2. Warum verwenden Unix-Manpages doppelte Backticks anstelle von doppelten Anführungszeichen?

  3. Was ist eine gute Mnemonik für Shell Double Vs. Einzelzitate?

  4. Wie fügt man Zeilenumbrüche in Variablen im Bash-Skript ein?

  5. Einschließen Vs. Den Wert einer Variablen nicht in Anführungszeichen in Bash setzen?

Wann verwendet man Nohup?

Wann Sie einen dedizierten Server verwenden sollten

Entfernen von einfachen und doppelten Anführungszeichen in einer Zeichenfolge nur mit Bash / Standard-Linux-Befehlen

Wann soll eine Shell-Variable in Anführungszeichen gesetzt werden?

So entfernen Sie die doppelten Anführungszeichen in einer CSV-Datei

Unterstrich durch Komma ersetzen und doppelte Anführungszeichen in CSV entfernen