Früher war ich mir sicher, dass das Zitieren von Zeichenfolgen immer eine gute Methode ist, um zu vermeiden, dass die Shell sie analysiert.
Dann bin ich auf folgendes gestoßen:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Beim Versuch, das Problem einzugrenzen, wird derselbe Fehler angezeigt:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Ich habe das Problem folgendermaßen gelöst:
[ "$x" = '1' ] && [ "$y" = '1' ]
Trotzdem muss ich wissen, was hier los ist.
[[ "$x" = '1' && "$y" = '1' ]]
-a
und ist-o
aus diesem Grund veraltet (was der[OB]
hochgestellte Text neben der Spezifikation bedeutet). Wenn Sie[ "$x" = 1 ] && [ "$y" = 1 ]
stattdessen schreiben, geht es Ihnen gut und Sie befinden sich im Bereich eines klar definierten / standardisierten Verhaltens.[ "x$x" = "x1" ]
zu verhindern, dass Argumente als Operatoren falsch interpretiert werden.dash
anstelle von Bash verwendet, ist dies immer noch eine nützliche Übung.Antworten:
Dies ist ein sehr undurchsichtiger Eckfall, bei dem man einen Fehler in der
[
Definition des integrierten Tests in Betracht ziehen könnte . Es entspricht jedoch dem Verhalten der tatsächlichen[
Binärdatei, die auf vielen Systemen verfügbar ist. Soweit ich sagen kann, es wirkt sich nur auf bestimmte Fälle und eine Variable einen Wert hat, eine übereinstimmt[
Operator wie(
,!
,=
,-e
, und so weiter.Lassen Sie mich erklären, warum und wie man es in Bash- und POSIX-Shells umgeht.
Erläuterung:
Folgendes berücksichtigen:
Kein Problem; das obige ergibt keinen Fehler und gibt aus
yes
. So erwarten wir, dass Dinge funktionieren. Sie können die Vergleichszeichenfolge'1'
wie gewünscht und den Wert von in ändernx
, und es funktioniert wie erwartet.Beachten Sie, dass sich die eigentliche
/usr/bin/[
Binärdatei genauso verhält. Wenn Sie zB laufen'/usr/bin/[' '(' = '(' ']'
liegt kein Fehler vor, da das Programm erkennen kann, dass die Argumente aus einer einzelnen Zeichenfolgenvergleichsoperation bestehen.Der Fehler tritt auf, wenn wir und mit einem zweiten Ausdruck. Es spielt keine Rolle, was der zweite Ausdruck ist, solange er gültig ist. Beispielsweise,
gibt aus
yes
und ist offensichtlich ein gültiger Ausdruck; aber wenn wir die beiden kombinieren,Bash lehnt den Ausdruck , wenn und nur wenn
x
ist(
oder!
.Wenn wir das oben genannte unter Verwendung des tatsächlichen
[
Programms ausführen würden , d.h.Der Fehler wäre verständlich: Da die Shell die Variablensubstitutionen ausführt, die
/usr/bin/[
Binärdatei nur Parameter empfängt(
=
(
-a
1
=
1
und beendet]
, kann sie verständlicherweise nicht analysieren, ob die offenen Klammern einen Unterausdruck beginnen oder nicht, da ein und vorhanden ist -Operation beteiligt ist. Sicher, das Parsen als zwei Zeichenfolgenvergleiche ist möglich, aber wenn Sie dies gierig tun, kann dies zu Problemen führen, wenn es auf korrekte Ausdrücke mit in Klammern gesetzten Unterausdrücken angewendet wird.Das eigentliche Problem ist, dass sich die
[
eingebaute Shell genauso verhält, als würde sie den Wert vonx
vor der Prüfung des Ausdrucks erweitern.(Diese und andere Unklarheiten im Zusammenhang mit der Variablenerweiterung waren ein wichtiger Grund für die Implementierung von Bash und empfehlen nun, stattdessen die
[[ ... ]]
Testausdrücke zu verwenden.)Die Problemumgehung ist trivial und wird häufig in Skripten mit älteren
sh
Shells verwendet. Sie fügen häufig ein "sicheres" Zeichenx
vor die Zeichenfolgen ein (beide Werte werden verglichen), um sicherzustellen, dass der Ausdruck als Zeichenfolgenvergleich erkannt wird:quelle
[
eingebauten nicht als Fehler bezeichnen. Wenn überhaupt, ist es ein inhärenter Konstruktionsfehler.[[
ist ein Shell- Schlüsselwort , nicht nur ein eingebauter Befehl, mit dem Sie vor dem Entfernen von Anführungszeichen einen Blick auf die Dinge werfen und die übliche Wortteilung überschreiben können. zB[[ $x == 1 ]]
muss nicht verwendet werden"$x"
, da sich ein[[
Kontext von normal unterscheidet. Auf diese Weise[[
können die Fallstricke vermieden werden[
. POSIX erfordert[
, wie es funktioniert verhalten, und bash ist meist POSIX - kompatibel , auch ohne--posix
, so ändert[
in ein Schlüsselwort unattraktiv ist.[
.[
das Verhalten durch die Historie vor POSIX belastet ist, habe ich stattdessen das letztere Wort gewählt.) Ich persönlich vermeide die Verwendung[[
in meinen Beispielskripten, aber nur, weil es ein Ausnahme von der Zitierempfehlung, über die ich immer harpere (da das Weglassen von Zitaten der häufigste Grund für Skriptfehler ist), und ich habe mir keinen einfachen Absatz ausgedacht, um zu erklären, warum[[
eine Ausnahme von den Regeln vorliegt, ohne meine Zitierempfehlung abzugeben vermuten.sh
Skripten verwendete historische Praxis , die der POSIX-Standardisierung weit voraus ist. Mit geklammerten Unterausdrücken und-a
und-o
logische Operatoren in Tests /[
ist effizienter , dass auf der Expression Chaining unter Berufung (über&&
und||
); Es ist nur so, dass bei aktuellen Maschinen der Unterschied unerheblich ist.&&
und zu verketten||
, aber der Grund dafür ist, dass es für uns Menschen einfacher ist, sie zu pflegen (zu lesen, zu verstehen und zu ändern, falls erforderlich), ohne Fehler einzuführen. Ich kritisiere also nicht Ihren Vorschlag, sondern nur die Gründe für den Vorschlag. (Bei sehr ähnlichen Gründen die.LT.
,.GE.
etc. Vergleichsoperator in Fortran 77 bekamen viel mehr menschenfreundliche Versionen<
,>=
usw. in späteren Versionen.)[
akatest
sieht:test
akzeptiert Unterausdrücke in Klammern; Daher wird angenommen, dass die linke Klammer einen Unterausdruck öffnet und versucht, ihn zu analysieren. Der Parser sieht das=
als erstes in dem Unterausdruck und denkt, dass es sich um einen impliziten Stringlängentest handelt, also ist er glücklich. Auf den Unterausdruck sollte dann eine rechte Klammer folgen, und stattdessen findet der Parser1
statt)
. Und es beschwert sich.Wenn
test
genau drei Argumente vorhanden sind und das mittlere Argument einer der erkannten Operatoren ist, wendet es diesen Operator auf das erste und dritte Argument an, ohne nach Unterausdrücken in Klammern zu suchen.Für die vollständigen Details schauen Sie auf
man bash
, suchen Sie nachtest expr
.Fazit: Der von verwendete Parsing-Algorithmus
test
ist kompliziert. Verwenden Sie nur einfache Ausdrücke und verwenden Sie die Shell - Operatoren!
,&&
und||
sie zu kombinieren.quelle