920 Stimmen

Sind doppelte eckige Klammern [[ ]] gegenüber einfachen eckigen Klammern [ ] in der Bash vorzuziehen?

Ein Kollege behauptete kürzlich bei einer Codeüberprüfung, dass die [[ ]] Konstrukt ist zu bevorzugen gegenüber [ ] in Konstrukten wie

if [ "`id -nu`" = "$someuser" ] ; then
     echo "I love you madly, $someuser"
fi

Er konnte es nicht begründen. Gibt es eine?

32 Stimmen

Seien Sie flexibel, erlauben Sie sich manchmal, einen Ratschlag anzuhören, ohne dass Sie seine tiefgründige Erklärung benötigen :) Was die [[ damit ist der Code gut und klar, aber denken Sie an den Tag, an dem Sie Ihre Skriptwerke auf das System mit der Standard-Shell portieren werden, die nicht bash o ksh , usw. [ ist hässlicher und umständlicher, funktioniert aber wie AK-47 in jeder Situation.

404 Stimmen

@rook Man kann sich einen Ratschlag auch ohne eine ausführliche Erklärung anhören, klar. Aber wenn man um eine Erklärung bittet und sie nicht bekommt, ist das normalerweise ein Warnsignal. "Vertraue, aber überprüfe" und so weiter.

21 Stimmen

Siehe auch: [Was ist der Unterschied zwischen den Bash-Operatoren [[ vs [ vs ( vs ((?](https://unix.stackexchange.com/q/306111/201820) unter Unix und Linux SE.

870voto

Johannes Schaub - litb Punkte 479831

[[ birgt weniger Überraschungen und ist im Allgemeinen sicherer in der Anwendung. Aber es ist nicht portabel - POSIX spezifiziert nicht, was es tut und nur einige Shells unterstützen es (neben bash, ich habe gehört, dass ksh es auch unterstützt). Sie können zum Beispiel Folgendes tun

[[ -e $b ]]

um zu prüfen, ob eine Datei existiert. Aber mit [ müssen Sie zitieren $b denn es spaltet das Argument auf und erweitert Dinge wie "a*" (wobei [[ wörtlich nimmt). Das hat auch damit zu tun, wie [ kann ein externes Programm sein und erhält sein Argument ganz normal wie jedes andere Programm (obwohl es auch ein builtin sein kann, aber dann hat es immer noch nicht diese spezielle Behandlung).

[[ hat auch einige andere nette Funktionen, wie den Abgleich regulärer Ausdrücke mit =~ zusammen mit Operatoren, wie sie in C-ähnlichen Sprachen bekannt sind. Hier ist eine gute Seite darüber: [Was ist der Unterschied zwischen test, [ y [[ ?](http://mywiki.wooledge.org/BashFAQ/031) y Bash-Tests

44 Stimmen

In Anbetracht der Tatsache, dass Bash heutzutage überall zu finden ist, bin ich der Meinung, dass es verdammt portabel ist. Die einzige gängige Ausnahme für mich ist auf Busybox-Plattformen - aber angesichts der Hardware, auf der es läuft, würde man sich wahrscheinlich sowieso besonders anstrengen wollen.

31 Stimmen

@guns: In der Tat. Ich würde vorschlagen, dass Ihr zweiter Satz Ihren ersten widerlegt; wenn Sie die Softwareentwicklung als Ganzes betrachten, sind "Bash ist überall" und "die Ausnahme sind busybox-Plattformen" völlig unvereinbar. busybox ist für die Embedded-Entwicklung weit verbreitet.

15 Stimmen

@guns: Selbst wenn die Bash auf meinem Rechner installiert ist, kann es sein, dass sie das Skript nicht ausführt (vielleicht habe ich /bin/sh mit einer Bash-Variante symverlinkt, wie es bei FreeBSD und Ubuntu üblich ist). Es gibt auch noch weitere Merkwürdigkeiten mit Busybox: Mit den heutigen Standardkompilierungsoptionen analysiert es [[ ]] sondern interpretiert es so, dass es dasselbe bedeutet wie [ ] .

406voto

Unterschiedliche Verhaltensweisen

Einige Unterschiede zu Bash 4.3.11:

  • POSIX vs. Bash-Erweiterung:

  • regulärer Befehl vs. Magie

    • [ ist nur ein normaler Befehl mit einem seltsamen Namen.

      ] ist lediglich das letzte Argument von [ .

      Ubuntu 16.04 hat sogar eine ausführbare Datei dafür unter /usr/bin/[ zur Verfügung gestellt von coreutils aber die in der Bash eingebaute Version hat Vorrang.

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

      Im Besonderen, < ist die Umleitung, && y || mehrere Befehle zu verketten, ( ) erzeugt Unterschalen, es sei denn, sie werden durch \ , und die Worterweiterung erfolgt wie üblich.

    • [[ X ]] ist ein einziges Konstrukt, das die X auf magische Weise geparst werden. < , && , || y () werden besonders behandelt, und es gelten andere Regeln für die Worttrennung.

      Es gibt noch weitere Unterschiede wie = y =~ .

    In Bashese: [ ist ein eingebauter Befehl, und [[ ist ein Schlüsselwort: Was ist der Unterschied zwischen Shell-Builtin und Shell-Schlüsselwort?

  • <

  • && y ||

    • [[ a = a && b = b ]] : wahr, logisch y
    • [ a = a && b = b ] : Syntaxfehler, && wird als AND-Befehlstrennzeichen geparst cmd1 && cmd2
    • [ a = a ] && [ b = b ] : POSIX zuverlässiges Äquivalent
    • [ a = a -a b = b ] : fast gleichwertig, aber von POSIX abgelehnt, weil es unsinnig ist und bei einigen Werten von a o b wie ! o ( die als logische Operationen interpretiert werden würden
  • (

    • [[ (a = a || a = b) && a = b ]] : falsch. Ohne ( ) es wäre wahr, denn [[ && ]] hat einen höheren Vorrang als [[ || ]]
    • [ ( a = a ) ] : Syntaxfehler, () wird als Unterschale interpretiert
    • [ \( a = a -o a = b \) -a a = b ] : gleichwertig, aber () , -a et -o sind von POSIX veraltet. Ohne \( \) es wäre wahr, denn -a hat einen höheren Vorrang als -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] nicht veraltetes POSIX-Äquivalent. In diesem speziellen Fall hätten wir jedoch auch einfach schreiben können: [ a = a ] || [ a = b ] && [ a = b ] denn die || y && Shell-Operatoren haben gleichen Vorrang, im Gegensatz zu [[ || ]] y [[ && ]] y -o , -a y [
  • Worttrennung und Generierung von Dateinamen bei Expansionen (Split+Glob)

    • x='a b'; [[ $x = 'a b' ]] : wahr. Anführungszeichen werden nicht benötigt
    • x='a b'; [ $x = 'a b' ] : Syntaxfehler. Er erweitert sich zu [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ] : Syntaxfehler, wenn es mehr als eine Datei im aktuellen Verzeichnis gibt.
    • x='a b'; [ "$x" = 'a b' ] : POSIX-Äquivalent
  • =

    • [[ ab = a? ]] : wahr, denn es ist Mustervergleich ( * ? [ sind magisch). Erweitert nicht auf Dateien im aktuellen Verzeichnis.
    • [ ab = a? ] : a? glob ausdehnt. Es kann also wahr oder falsch sein, je nachdem, welche Dateien sich im aktuellen Verzeichnis befinden.
    • [ ab = a\? ] : false, keine Globalexpansion
    • = y == sind in beiden Fällen die gleichen [ y [[ aber == ist eine Bash-Erweiterung.
    • case ab in (a?) echo match; esac : POSIX-Äquivalent
    • [[ ab =~ 'ab?' ]] : false, verliert die Magie mit '' in Bash 3.2 und höher und sofern die Kompatibilität zu Bash 3.1 nicht aktiviert ist (wie bei BASH_COMPAT=3.1 )
    • [[ ab? =~ 'ab?' ]] : wahr
  • =~

    • [[ ab =~ ab? ]] : wahr. POSIX erweiterter regulärer Ausdruck Spiel und ? expandiert nicht global
    • [ a =~ a ] : Syntaxfehler. 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: Verwenden Sie immer []

Es gibt POSIX-Äquivalente für jede [[ ]] Konstrukt, das ich gesehen habe.

Wenn Sie [[ ]] Sie:

  • Portabilität verlieren
  • den Leser zwingen, die Feinheiten einer anderen Bash-Erweiterung zu lernen. [ ist nur ein normaler Befehl mit einem seltsamen Namen, und es gibt keine besondere Semantik.

Dank an Stéphane Chazelas für wichtige Korrekturen und Ergänzungen.

2 Stimmen

Gilt: "Alles, was in POSIX funktioniert, funktioniert auch in BASH, aber nicht umgekehrt." ?

2 Stimmen

@Wlad Bash erweitert POSIX stark, daher ist jede Bash-Erweiterung nicht POSIX. Umgekehrt bin ich mir nicht 100% sicher, aber ich halte es für wahrscheinlich (außer wenn Bash-Erweiterungen eine POSIX-Syntax außer Kraft setzen, z.B. in posix [[ wäre vermutlich auch ein regulärer Befehl). Verwandt: askubuntu.com/questions/766270/

0 Stimmen

@PauloPedroso obrigado! Es gibt sowohl Schönheit als auch Schrecken bei der Beherrschung von Legacy-Technologie wie Bash.

73voto

[[ ]] hat mehr Funktionen - ich schlage vor, Sie werfen einen Blick auf die Leitfaden für fortgeschrittene Bash-Skripterstellung für weitere Informationen, insbesondere die erweiterter Prüfbefehl Abschnitt in Kapitel 7. Tests .

Im Übrigen, wie der Leitfaden anmerkt, [[ ]] wurde in ksh88 eingeführt (die Version von 1988 von KornShell ).

8 Stimmen

Dies ist keineswegs ein Bashismus, sondern wurde erstmals in der Korn-Shell eingeführt.

11 Stimmen

@Thomas, das ABS gilt in vielen Kreisen als sehr schlechtes Nachschlagewerk. Es enthält zwar viele genaue Informationen, achtet aber kaum darauf, schlechte Praktiken in seinen Beispielen zu vermeiden, und wurde lange Zeit nicht gewartet.

2 Stimmen

@CharlesDuffy danke für Ihren Kommentar, können Sie eine gute Alternative nennen. Ich bin kein Experte für Shell-Skripte, ich suche einen Leitfaden, den ich etwa einmal im halben Jahr zu Rate ziehen kann, um ein Skript zu schreiben.

24voto

crizCraig Punkte 7700

Wenn Sie sich für folgende Themen interessieren Google's Stilrichtlinien :

Test, [ … ] et [[ … ]]

[[ … ]] ist vorzuziehen gegenüber [ … ] , test y /usr/bin/[ .

[[ … ]] reduziert Fehler, da keine Pfadnamen-Erweiterung oder Worttrennung zwischen [[ y ]] . Darüber hinaus, [[ … ]] ermöglicht den Abgleich mit regulären Ausdrücken, während [ … ] nicht.

# This ensures the string on the left is made up of characters in
# the alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi

# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi

Für die blutigen Details siehe E14 unter http://tiswww.case.edu/php/chet/bash/FAQ

3 Stimmen

Google schrieb " Es findet keine Erweiterung des Pfadnamens statt... " noch [[ -d ~ ]] true zurückgibt (was bedeutet, dass ~ wurde erweitert auf /home/user ). Ich denke, Google hätte sich präziser ausdrücken müssen.

2 Stimmen

@JamesThomasMoon1979 Dies ist eine Tilde-Erweiterung, keine Pfadnamen-Erweiterung, die im Google-Text erwähnt wird.

0 Stimmen

Anführungszeichen um "Dateiname" innerhalb von [[ ]] sind nicht erforderlich.

23voto

f3lix Punkte 28602

De Welcher Komparator, Test, Klammer oder Doppelklammer, ist am schnellsten? :

Die doppelte Klammer ist eine "Verbindung Befehl", während test und die einfache Klammer Shell-Bausteine sind (und in eigentlich derselbe Befehl sind). Also die einfache Klammer und die doppelte Klammer unterschiedlichen Code ausführen.

Der Test und die einzelne Klammer sind die am besten portabel, da sie als separate und externe Befehle existieren. Wenn Sie jedoch eine auch nur annähernd moderne Version von BASH verwenden, wird die doppelte Klammer unterstützt.

11 Stimmen

Was hat es mit der Besessenheit von schnellste in Shell-Skripten? Ich möchte es am tragbarsten und sich um die Verbesserung nicht scheren [[ bringen könnte. Aber ich bin ja auch ein alter Hase :-)

1 Stimmen

Ich glaube, viele Shells bieten integrierte Versionen von [ y test obwohl es auch externe Versionen gibt.

4 Stimmen

@Jens im Allgemeinen stimme ich zu: der ganze Zweck von Skripten ist (war?) Portabilität (sonst würden wir codieren und kompilieren, nicht skripten)... die beiden Ausnahmen, die mir einfallen, sind: (1) Tabulatorvervollständigung (wo Vervollständigungsskripte mit viel bedingter Logik sehr lang werden können); und (2) Superprompts ( PS1=...crazy stuff... und/oder $PROMPT_COMMAND ); für diese möchte ich keine wahrnehmbare Verzögerung bei der Ausführung des Skripts.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X