Ich bin ein bisschen verwirrt darüber, was diese Operatoren anders machen, wenn sie in Bash verwendet werden (Klammern, doppelte Klammern, Klammern und doppelte Klammern).
[[ , [ , ( , ((
Ich habe Leute gesehen, die sie benutzt haben, wenn Aussagen wie diese:
if [[condition]]
if [condition]
if ((condition))
if (condition)
Antworten:
Eine
if
Aussage sieht normalerweise so ausDie
then
Klausel wird ausgeführt, wenn der Exit-Code voncommands1
Null ist. Wenn der Exit-Code ungleich Null ist, wird dieelse
Klausel ausgeführt.commands1
kann einfach oder komplex sein. Es kann zum Beispiel eine Folge von einer oder mehreren Rohrleitungen von einem der Operatoren getrennt sind;
,&
,&&
, oder||
. Dieif
unten aufgeführten Bedingungen sind nur Sonderfälle voncommands1
:if [ condition ]
Dies ist der traditionelle Shell -
test
Befehl. Es ist auf allen POSIX-Shells verfügbar. Der Testbefehl setzt einen Exit-Code und dieif
Anweisung verhält sich entsprechend. Typische Tests sind, ob eine Datei existiert oder eine Zahl mit einer anderen übereinstimmt.if [[ condition ]]
Dies ist eine neue aktualisierte Variante
test
von ksh , die auch bash und zsh unterstützt. Diesertest
Befehl setzt auch einen Exit-Code und dieif
Anweisung verhält sich entsprechend. Unter den erweiterten Funktionen kann getestet werden, ob eine Zeichenfolge mit einem regulären Ausdruck übereinstimmt.if ((condition))
Eine weitere ksh- Erweiterung, die auch bash und zsh unterstützt. Dies führt eine Arithmetik durch. Als Ergebnis der Arithmetik wird ein Exit-Code gesetzt und die
if
Anweisung entsprechend ausgeführt. Es gibt einen Exit-Code von Null (true) zurück, wenn das Ergebnis der arithmetischen Berechnung ungleich Null ist. Ebenso[[...]]
ist dieses Formular nicht POSIX und daher nicht portierbar.if (command)
Dadurch wird der Befehl in einer Subshell ausgeführt. Nach Abschluss des Befehls wird ein Beendigungscode festgelegt und die
if
Anweisung entsprechend ausgeführt.Ein typischer Grund für ein Sub - Shell wie dies mit zu Nebenwirkungen zu begrenzen ,
command
wenncommand
Variablenzuweisungen oder andere Änderungen erforderlich , um die Umgebung des Shell. Solche Änderungen bleiben nach Abschluss der Subshell nicht erhalten.if command
Befehl wird ausgeführt und die
if
Anweisung verhält sich entsprechend ihrem Exit-Code.quelle
[
es sich tatsächlich um eine Binärdatei handelt, nicht um einen internen Befehl oder ein internes Symbol. Lebt in der Regel in/bin
.[
ist eingebauttest
. Aus Kompatibilitätsgründen sind Binärversionen verfügbar. Check outhelp [
undhelp test
.$((
dh arithmetische Erweiterung ist und es leicht ist, sie zu verwechseln. Oft besteht eine[ $((2+2)) -eq 4 ]
Problemumgehung darin, in bedingten Anweisungen so etwas wie die Verwendung von Arithmetik zu verwenden(…)
Klammern kennzeichnen eine Unterschale . Was in ihnen steckt, ist kein Ausdruck wie in vielen anderen Sprachen. Es ist eine Liste von Befehlen (genau wie außerhalb von Klammern). Diese Befehle werden in einem separaten Unterprozess ausgeführt, sodass alle innerhalb der Klammern ausgeführten Umleitungen, Zuweisungen usw. keine Auswirkungen außerhalb der Klammern haben.$(…)
ist eine Befehlsersetzung : In den Klammern steht ein Befehl, und die Ausgabe des Befehls wird als Teil der Befehlszeile verwendet (nach zusätzlichen Erweiterungen, es sei denn, die Ersetzung erfolgt zwischen doppelten Anführungszeichen, aber das ist eine andere Geschichte ). .{ … }
Klammern ähneln Klammern, da sie Befehle gruppieren, aber sie beeinflussen nur das Parsen, nicht das Gruppieren. Das Programm gibtx=2; { x=4; }; echo $x
4 aus, währendx=2; (x=4); echo $x
2 ausgegeben wird. (Auch geschweifte Klammern als Schlüsselwörter müssen begrenzt und an der Befehlsposition gefunden werden (daher das Leerzeichen nach{
und;
vor}
), während Klammern dies nicht tun. Das ist nur eine Syntax-Eigenheit.)${VAR}
handelt es sich um eine Parametererweiterung , die mit möglichen zusätzlichen Transformationen auf den Wert einer Variablen erweitert wird. Dieksh93
Shell unterstützt auch eine${ cmd;}
Form der Befehlsersetzung, die keine Subshell erzeugt.((…))
Doppelte Klammern umgeben eine arithmetische Anweisung , dh eine Berechnung mit ganzen Zahlen, mit einer Syntax, die anderen Programmiersprachen ähnelt. Diese Syntax wird hauptsächlich für Zuweisungen und in Bedingungen verwendet. Dies gibt es nur in ksh / bash / zsh, nicht in plain sh.$((…))
, die auf den ganzzahligen Wert des Ausdrucks erweitert werden.[ … ]
einzelne Klammern umgeben bedingte Ausdrücke . Bedingte Ausdrücke basieren hauptsächlich auf Operatoren , um-n "$variable"
zu testen, ob eine Variable leer ist und-e "$file"
ob eine Datei vorhanden ist. Beachten Sie, dass Sie ein Leerzeichen um jeden Operator (z. B.[ "$x" = "$y" ]
nicht) und ein Leerzeichen oder ein Zeichen wie[ "$x"="$y" ]
;
innerhalb und außerhalb der Klammern (z . B.[ -n "$foo" ]
nicht) benötigen .[-n "$foo"]
[[ … ]]
Doppelte Klammern sind eine alternative Form bedingter Ausdrücke in ksh / bash / zsh mit einigen zusätzlichen Funktionen. Sie können beispielsweise schreiben[[ -L $file && -f $file ]]
, um zu testen, ob eine Datei eine symbolische Verknüpfung zu einer regulären Datei ist, während einzelne Klammern dies erfordern[ -L "$file" ] && [ -f "$file" ]
. Siehe Warum funktioniert die Parametererweiterung mit Leerzeichen ohne Anführungszeichen in doppelten Klammern [[aber nicht in einfachen Klammern [? für mehr zu diesem Thema.In der Shell ist jeder Befehl ein bedingter Befehl: Jeder Befehl hat einen Rückgabestatus, der entweder 0 ist, um Erfolg anzuzeigen, oder eine Ganzzahl zwischen 1 und 255 (und möglicherweise mehr in einigen Shells), um einen Fehler anzuzeigen. Der
[ … ]
Befehl (oder die[[ … ]]
Syntaxform) ist ein bestimmter Befehl, der auch geschrieben werden kanntest …
und erfolgreich ist, wenn eine Datei vorhanden ist, wenn eine Zeichenfolge nicht leer ist oder wenn eine Zahl kleiner als eine andere ist usw. Die((…))
Syntaxform ist erfolgreich, wenn eine Zahl vorhanden ist ist ungleich Null. Hier einige Beispiele für Bedingungen in einem Shell-Skript:Testen Sie, ob
myfile
die Zeichenfolge enthalten isthello
:Wenn
mydir
es sich um ein Verzeichnis handelt, wechseln Sie in dieses und erledigen Sie Folgendes:Testen Sie, ob sich
myfile
im aktuellen Verzeichnis eine Datei befindet :Das gleiche, aber auch herabhängende symbolische Links:
Testen Sie, ob der Wert von
x
(der als numerisch angenommen wird) mindestens 2 ist, tragbar:Testen Sie
x
in bash / ksh / zsh, ob der Wert von (der als numerisch angenommen wird) mindestens 2 beträgt:quelle
-a
anstelle von unterstützen&&
, so dass man schreiben kann:[ -L $file -a -f $file ]
Dies ist die gleiche Anzahl von Zeichen in den Klammern ohne das Extra[
und]
...-a
und-o
sind problematisch, weil sie zu falschen Parses führen können, wenn einige der beteiligten Operanden wie Operatoren aussehen. Deshalb erwähne ich sie nicht: Sie haben keinen Vorteil und funktionieren nicht immer. Und schreiben Sie niemals ohne guten Grund nicht zitierte Variablenerweiterungen:[[ -L $file -a -f $file ]]
ist in Ordnung, aber mit einzelnen Klammern[ -L "$file" -a -f "$file" ]
(was zB in Ordnung ist, wenn$file
immer mit/
oder begonnen wird./
).[[ -L $file && -f $file ]]
(nein-a
mit der[[...]]
Variante).Aus der Bash-Dokumentation :
Mit anderen Worten, Sie stellen sicher, dass das, was auch immer in 'list' (wie a
cd
) passiert, keine Auswirkung außerhalb von(
und hat)
. Die einzige Sache , die undicht wird , ist der Exit - Code des letzten Befehls oder die mitset -e
dem ersten Befehl, der einen Fehler (außer ein paar wie erzeugtif
,while
usw.)Dies ist eine Bash-Erweiterung, mit der Sie rechnen können. Dies ähnelt in gewisser Weise der Verwendung
expr
ohne alle Einschränkungen vonexpr
(z. B. überall Leerzeichen, Flucht*
usw.).Dies bietet einen erweiterten Test zum Vergleichen von Zeichenfolgen, Zahlen und Dateien, der den
test
Angeboten ähnelt, jedoch leistungsfähiger ist.Dieser ruft
test
. Eigentlich war in alten Zeiten[
eine symbolische Verbindung zutest
. Es funktioniert genauso und Sie haben die gleichen Einschränkungen. Da eine Binärdatei den Namen kennt, mit dem sie gestartet wurde, kann das Testprogramm Parameter analysieren, bis es einen Parameter findet]
. Lustige Unix-Tricks.Beachten Sie, dass im Fall
bash
,[
undtest
sind integrierte Funktionen (wie in einem Kommentar erwähnt), aber so ziemlich die gleichen Einschränkungen gelten.quelle
test
und[
natürlich in Bash eingebaute Befehle sind, ist es wahrscheinlich, dass auch eine externe Binärdatei existiert.[
isttest
auf den meisten modernen Systemen keine symbolische Verknüpfung .strings /usr/bin/test
den Hilfetext enthält, weiß ich nicht, was ich sagen soll.test
Befehl nach dem Standard offensichtlich als eigenständiger dateibasierter Befehl existieren muss, gibt nichts in ihm an, dass seine[
Variante auch auf diese Weise implementiert werden muss. Zum Beispiel stellt Solaris 11 keine[
ausführbare Datei zur Verfügung, ist aber dennoch vollständig mit den POSIX-Standards kompatibel[
vs[[
Diese Antwort deckt die
[
vs-[[
Teilmenge der Frage ab.Einige Unterschiede zu Bash 4.3.11:
POSIX vs Bash-Erweiterung:
[
ist POSIX[[
ist eine Bash-Erweiterung¹, die unter https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs dokumentiert istRegelmäß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, dasX
magisch 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 ?<
[[ a < b ]]
: lexikographischer Vergleich[ a \< b ]
: Das gleiche wie oben.\
erforderlich oder führt die Umleitung wie bei jedem anderen Befehl aus. Bash-Erweiterung.expr a \< b > /dev/null
: POSIX-Äquivalent², siehe: https://stackoverflow.com/questions/21294867/how-to-test-strings-for-lexicographic-less-than-or-equal-in-bash/52707989#52707989&&
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 5Wortteilung und Generierung von Dateinamen bei Erweiterungen (split + glob)
x='a b'; [[ $x = 'a b' ]]
: true, keine Anführungszeichen erforderlichx='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
[[ ]]
:[
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
a
oderb
(wie+
oderindex
) fehl und führt einen numerischen Vergleich durch, wenna
undb
wie Dezimalzahlen aussehen.expr "x$a" '<' "x$b"
funktioniert um beide.³ und fällt auch für einige Werte von
a
oderb
wie!
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.quelle
expr
die Antwort hinzugefügt . Der Begriff "Bash-Erweiterung" bedeutet nicht, dass Bash die erste Shell war, die eine Syntax hinzufügte. Das Erlernen von POSIX sh vs Bash reicht bereits aus, um mich in den Wahnsinn zu treiben.man test
Sie nach, ob Sie es versucht habenman [
und sich verlaufen haben. Das erklärt die POSIX-Variante.Einige Beispiele:
Traditioneller Test:
test
und[
sind Befehle wie alle anderen, daher wird die Variable in Wörter aufgeteilt, sofern sie nicht in Anführungszeichen steht.Neuer Test
[[ ... ]]
ist ein (neueres) spezielles Shell-Konstrukt, das ein bisschen anders funktioniert. Das offensichtlichste ist, dass es keine wortgeteilten Variablen gibt:Einige Dokumentationen dazu
[
und[[
hier .Arithmetischer Test:
"Normal" -Befehle:
Alle oben genannten Befehle verhalten sich wie normale Befehle und
if
können jeden Befehl annehmen:Mehrere Befehle:
Oder wir können mehrere Befehle verwenden. Durch das Einschließen einer Reihe von Befehlen werden
( ... )
diese in der Subshell ausgeführt und eine temporäre Kopie des Shell-Status (Arbeitsverzeichnis, Variablen) erstellt. Wenn wir ein Programm vorübergehend in einem anderen Verzeichnis ausführen müssen:quelle
Gruppierungsbefehle
( list )
Wenn Sie eine Befehlsliste in Klammern setzen, wird eine Subshell-Umgebung erstellt und alle Befehle in der Liste in dieser Subshell ausgeführt. Da die Liste in einer Subshell ausgeführt wird, bleiben die Variablenzuweisungen nach Abschluss der Subshell nicht wirksam.{ list; }
Wenn Sie eine Befehlsliste in geschweifte Klammern setzen, wird die Liste im aktuellen Shell-Kontext ausgeführt . Es wird keine Subshell erstellt. Die folgende Liste mit Semikolon (oder Zeilenvorschub) ist erforderlich. QuelleBedingte Konstrukte
Einzelne Klammer dh
[]
Zum Vergleich
==, !=, <,
und>
und sollte verwendet werden und zum numerischen Vergleicheq, ne,lt
undgt
sollte verwendet werden.Erweiterte Klammern dh
[[]]
In allen obigen Beispielen haben wir nur einfache Klammern verwendet, um den bedingten Ausdruck einzuschließen, aber bash erlaubt doppelte Klammern, die als erweiterte Version der Syntax für einfache Klammern dienen.
Zum Vergleich
==, !=, <,
und>
im wahrsten Sinne des Wortes.[
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.[[
wird verstanden unterKorn
undBash
.Quelle
quelle