Was ist der Unterschied zwischen doppelten und einfachen eckigen Klammern in Bash?

426

Ich habe mich nur gefragt, was genau der Unterschied ist

[[ $STRING != foo ]]

und

[ $STRING != foo ]

ist, abgesehen davon, dass das letztere posix-konform ist, gefunden in sh und das erstere ist eine Erweiterung, die in bash gefunden wird.

0x89
quelle
1
Falls Sie sich auch gefragt haben if, ob
Kev
Aus Ubuntus Dokumenten: wiki.ubuntu.com/…
radistao

Antworten:

309

Es gibt verschiedene Unterschiede. Meiner Meinung nach sind einige der wichtigsten:

  1. [ist eine eingebaute in Bash und viele andere moderne Muscheln. Das eingebaute [ähnelt testmit dem zusätzlichen Erfordernis eines Schließens ]. Die eingebauten [und testimitierten Funktionen /bin/[und /bin/testderen Einschränkungen, so dass Skripte abwärtskompatibel wären. Die ursprünglichen ausführbaren Dateien sind größtenteils noch aus Gründen der POSIX-Kompatibilität und Abwärtskompatibilität vorhanden. Wenn Sie den Befehl type [in Bash [ausführen, wird dies standardmäßig als eingebaut interpretiert. (Hinweis: Sucht which [nur nach ausführbaren Dateien auf dem Pfad und ist äquivalent zu type -p [)
  2. [[ist nicht so kompatibel, es wird nicht unbedingt funktionieren mit welchen /bin/shPunkten auch immer . So [[ist die modernere Bash / Zsh / Ksh-Option.
  3. Da diese Funktion [[in die Shell integriert ist und keine älteren Anforderungen stellt, müssen Sie sich keine Gedanken über die Wortteilung auf der Grundlage der IFS- Variablen machen, um Variablen durcheinanderzubringen, die als Zeichenfolge mit Leerzeichen ausgewertet werden. Daher müssen Sie die Variable nicht in doppelte Anführungszeichen setzen.

Der Rest ist größtenteils nur eine nette Syntax. Um weitere Unterschiede zu sehen, empfehle ich diesen Link zu einer FAQ-Antwort: Was ist der Unterschied zwischen test, [und [[? . In der Tat empfehle ich, wenn Sie es mit Bash-Skripten ernst meinen, das gesamte Wiki zu lesen , einschließlich der FAQ, Fallstricke und Anleitung. Der Testabschnitt aus dem Handbuchabschnitt erklärt auch diese Unterschiede und warum die Autoren der Meinung [[sind, dass dies die bessere Wahl ist, wenn Sie sich keine Gedanken über die Mobilität machen müssen. Die Hauptgründe sind:

  1. Sie müssen sich nicht darum kümmern, die linke Seite des Tests in Anführungszeichen zu setzen, damit er tatsächlich als Variable gelesen wird.
  2. Sie müssen nicht kleiner als und größer als < >mit Backslashes umgehen, damit sie nicht als Eingabeumleitung gewertet werden, was einige Dinge durch das Überschreiben von Dateien durcheinander bringen kann. Dies geht wieder zurück zu [[einem eingebauten. Wenn [(test) ein externes Programm ist, müsste die Shell in der Art <und Weise, wie sie ausgewertet wird, eine Ausnahme machen und >nur, wenn /bin/testes aufgerufen wird, was eigentlich keinen Sinn ergibt.
Kyle Brandt
quelle
5
Danke, der Link zu den Bash-FAQ war genau das, wonach ich gesucht habe (wusste nichts über diese Seite, danke).
0x89
2
Ich habe Ihren Beitrag mit diesen Informationen bearbeitet, aber [und Test werden als integrierte Elemente ausgeführt. Die Builtins sollten / bin / [und / bin / test ersetzen, aber auch die Einschränkungen der Binärdateien reproduzieren. Der Befehl 'type [' überprüft, ob das eingebaute Programm verwendet wird. 'which [' sucht nur im PATH nach ausführbaren Dateien und ist äquivalent zu 'type -P ['
klynch
133

Zusamenfassend:

[ist eine Bash Builtin

[[]] sind bash Keywords

Schlüsselwörter: Schlüsselwörter sind wie integrierte Schlüsselwörter, der Hauptunterschied besteht jedoch darin, dass für sie spezielle Analyseregeln gelten. Beispiel: [ist eine integrierte Bash, während [[ein Bash-Schlüsselwort ist. Sie werden beide zum Testen von Dingen verwendet, aber da [[kein eingebautes, sondern ein Schlüsselwort ist, profitieren sie von einigen speziellen Parsing-Regeln, die es viel einfacher machen:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Das erste Beispiel gibt einen Fehler zurück, weil bash versucht, die Datei b an den Befehl [a] umzuleiten. Das zweite Beispiel macht tatsächlich das, was Sie erwarten. Das Zeichen <hat keine spezielle Bedeutung mehr für den Operator "Dateiumleitung".

Quelle: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

abhiomkar
quelle
3
[ist ein POSIX-Shell-Befehl; Es muss nicht eingebaut werden. Es ]ist nur ein Argument, nach dem dieser Befehl sucht, damit die Syntax ausgewogen ist. Der Befehl ist ein Synonym für, testaußer dass er testnicht nach einem Abschluss sucht ].
Kaz
81

Verhaltensunterschiede

Einige Unterschiede zu Bash 4.3.11:

  • POSIX vs Bash-Erweiterung:

  • Regelmäßiges Kommando gegen Magie

    • [ ist nur ein regulärer Befehl mit einem seltsamen Namen.

      ]Dies ist nur ein Argument [, das die Verwendung weiterer Argumente verhindert.

      Ubuntu 16.04 hat tatsächlich eine ausführbare Datei, /usr/bin/[die von coreutils zur Verfügung gestellt wird, aber die in Bash integrierte Version hat Vorrang.

      An der Art und Weise, wie Bash den Befehl analysiert, ändert sich nichts.

      Insbesondere werden durch <Umleitung &&und ||Verketten mehrerer Befehle ( )Unterschalen generiert, sofern diese nicht durch Escapezeichen geschützt werden \, und die Worterweiterung erfolgt wie gewohnt.

    • [[ X ]]ist ein einzelnes Konstrukt, das Xmagisch analysiert werden kann. <, &&, ||Und ()sind speziell, und einzelne Wörter aufgespalten Regeln behandelt unterschiedlich.

      Es gibt auch weitere Unterschiede wie =und =~.

      In Bashese: [ist ein integrierter Befehl und [[ein Schlüsselwort: https://askubuntu.com/questions/445749/was ist der Unterschied zwischen dem eingebauten Shell- und dem Shell - Schlüsselwort ?

  • <

  • && und ||

    • [[ a = a && b = b ]]: wahr, logisch und
    • [ a = a && b = b ]: Syntaxfehler, der &&als UND-Befehlstrennzeichen analysiert wirdcmd1 && cmd2
    • [ a = a -a b = b ]: äquivalent, aber von POSIX³ veraltet
    • [ a = a ] && [ b = b ]: POSIX und zuverlässiges Äquivalent
  • (

    • [[ (a = a || a = b) && a = b ]]: falsch
    • [ ( a = a ) ]: Syntaxfehler, ()wird als Subshell interpretiert
    • [ \( a = a -o a = b \) -a a = b ]: äquivalent, wird aber ()von POSIX nicht mehr unterstützt
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX-Äquivalent 5
  • Wortteilung und Generierung von Dateinamen bei Erweiterungen (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: true, keine Anführungszeichen erforderlich
    • x='a b'; [ $x = 'a b' ]: Syntaxfehler, erweitert auf [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: Syntaxfehler, wenn sich mehr als eine Datei im aktuellen Verzeichnis befindet.
    • x='a b'; [ "$x" = 'a b' ]: POSIX-Äquivalent
  • =

    • [[ ab = a? ]]: true, weil es Pattern Matching macht ( * ? [sind magisch). Wird nicht global auf Dateien im aktuellen Verzeichnis erweitert.
    • [ ab = a? ]: a?glob erweitert. Dies kann je nach den Dateien im aktuellen Verzeichnis wahr oder falsch sein.
    • [ ab = a\? ]: false, keine Glob-Erweiterung
    • =und ==sind in beiden [und gleich [[, ist aber ==eine Bash-Erweiterung.
    • case ab in (a?) echo match; esac: POSIX-Äquivalent
    • [[ ab =~ 'ab?' ]]: false 4 , verliert Magie mit''
    • [[ ab? =~ 'ab?' ]]: wahr
  • =~

    • [[ ab =~ ab? ]]: true, POSIX- Übereinstimmung mit erweiterten regulären Ausdrücken , ?Glob-Erweiterung nicht möglich
    • [ a =~ a ]: Syntax-Fehler. Kein Bash-Äquivalent.
    • printf 'ab\n' | grep -Eq 'ab?': POSIX-Äquivalent (nur einzeilige Daten)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX-Äquivalent.

Empfehlung : immer verwenden [].

Für jedes [[ ]]Konstrukt, das ich gesehen habe, gibt es POSIX-Entsprechungen .

Wenn Sie Sie benutzen [[ ]]:

  • Portabilität verlieren
  • den Leser zwingen, die Feinheiten einer anderen Bash-Erweiterung zu lernen. [ist nur ein regulärer Befehl mit einem seltsamen Namen, es ist keine spezielle Semantik involviert.

¹ Inspiriert von dem entsprechenden [[...]]Konstrukt in der Korn-Shell

² schlägt jedoch für einige Werte von aoder b(wie +oder index) fehl und führt einen numerischen Vergleich durch, wenn aund bwie Dezimalzahlen aussehen. expr "x$a" '<' "x$b"funktioniert um beide.

³ und fällt auch für einige Werte von aoder bwie !oder aus (.

4 in Bash 3.2 und höher und vorausgesetzt, die Kompatibilität zu Bash 3.1 ist nicht aktiviert (wie bei BASH_COMPAT=3.1)

5 obwohl die Gruppierung (hier mit der {...;}Befehlsgruppe, statt der (...)eine unnötige Subshell ausgeführt werden würde) nicht erforderlich ist, da die Operatoren ||und die &&Shell (im Gegensatz zu den Operatoren ||und && [[...]]oder den Operatoren -o/ -a [) den gleichen Vorrang haben. Wäre [ a = a ] || [ a = b ] && [ a = b ]also gleichwertig.

Ciro Santilli ist ein Schauspieler
quelle
Wie benutzt man printf 'ab' | grep -Eq 'ab?'innerhalb if [ … ]?
MeeDamian
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []ist ein Befehl wie grep. Das wird ()für diesen Befehl möglicherweise nicht benötigt. Ich bin mir nicht sicher: Ich habe es hinzugefügt, da es davon |abhängt, wie Bash die Dinge analysiert. Wenn es keine gäbe |, kannst du sicher nur schreiben if cmd arg arg; then.
Ciro Santilli
1
@meeDamian yeah, keine Notwendigkeit dafür ()scheint: stackoverflow.com/questions/8965509/…
Ciro Santilli Am
1
Schöne Liste! Siehe auch: wiki.ubuntu.com/…
radistao
5

Single Bracket dh []ist POSIX - Shell konform einen bedingten Ausdruck zu umschließen.

Doppel Brackets dh [[]]ist eine erweiterte (oder Erweiterung) Version von Standard - POSIX - Version, wird dies durch bash und andere Shells unterstützt wird (zsh, KSH).

In der bash für numerischen Vergleich verwenden wir eq, ne, ltund gt, mit doppelten Klammern zum Vergleich können wir verwenden ==, !=, <,und >buchstäblich.

  • [ist ein Synonym für Testbefehl. Selbst wenn es in die Shell integriert ist, wird ein neuer Prozess erstellt.
  • [[ ist eine neue verbesserte Version davon, die ein Schlüsselwort ist, kein Programm.

zum Beispiel:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works
Premraj
quelle
4

Basierend auf einem schnellen Lesen der entsprechenden Abschnitte der manpage, erscheint der Hauptunterschied der zu sein , dass ==und !=Betreiber Spiel gegen ein Muster, anstatt eine Zeichenkette, und auch , dass die es =~regex Vergleichsoperator.

womble
quelle