Interaktive, nicht interaktive Shells und Erweiterung

374
Glyph

Wenn ich den Test ausführen [[ ! (-z "") ]]interaktiv oder nicht interaktiv bash -c '[[ ! (-z "") ]]', erhalte ich das gleiche Ergebnis, das ist echo $?mir gibt 1.

Wenn ich jedoch ein Leerzeichen im obigen Ausdruck vergesse, erhalte [[ !(-z "") ]]ich nicht mehr dasselbe Ergebnis. Etwas präziser,

set -x; [[ !(-z "") ]]; echo $? 

gibt mir

+ [[ -n !(-z ) ]] + echo 0 0 

während

bash -c 'set -x; [[ !(-z "") ]]; echo $?' 

gibt mir

+ [[ -z '' ]] + echo 1 1 

Meine Frage ist also: Warum geben interaktive und nicht interaktive Shells nicht die gleiche Erweiterung und in diesem Fall dasselbe Ergebnis (oder ähnliche)?

1

1 Antwort auf die Frage

0
Kamil Maciorowski

Das ist ziemlich kompliziert.

Bitte beachten Sie, dass das Globbing im Inneren nicht funktioniert [[ ]]. Wenn du sowas rennst

set -x; [[ / == /* ]]; echo $? 

dann wirst du sehen /*wie dort nicht expandiert echo /*.

Jetzt gibt es eine extglobShell-Option. Es sieht so aus, als wäre es in Ihrem Fall in interaktiven Shells aktiviert, in nicht interaktiven deaktiviert. (Compare Wo ist bashs "shopt extglob" für meine interaktive Shell aktiviert? )

Wenn die extglobShell-Option mithilfe des integrierten shoptBefehls aktiviert wird, werden mehrere erweiterte Musteranpassungsoperatoren erkannt. […]

!(pattern-list)
Entspricht nichts außer einem der angegebenen Muster.

( Quelle )

[[ ]]Der Bediener wird im Inneren erkannt, erweitert sich jedoch nicht. Wie soll ich wissen? Ich kann die Shell dazu bringen, sie nicht zu erkennen:

shopt -u extglob set -x; [[ !(-z "") ]]; echo $? 

Jetzt bekomme ich -bash: !: event not found. Dies weist auf !eine andere Bedeutung hin:

!
Starten Sie eine Verlaufsersetzung, es sei denn, es folgt ein Leerzeichen, eine Registerkarte, das Ende der Zeile =oder ((wenn die extglobShell-Option mithilfe des integrierten shoptBefehls aktiviert ist).

( Quelle )

Dies unterscheidet sich zwischen interaktiven und nicht interaktiven Shells:

Wenn die Shell interaktiv ausgeführt wird, ändert sich ihr Verhalten auf verschiedene Arten.

[…]
7. Der Befehlsverlauf […] und die Erweiterung des Verlaufs […] sind standardmäßig aktiviert.

( Quelle )

Der nächste Schritt ist das Deaktivieren des Verlaufs:

shopt -u extglob set +o history set -x; [[ !(-z "") ]]; echo $? 

Und hier ist das Ergebnis wie in Ihrer nicht interaktiven Shell.


Können wir das andersherum machen? Können wir eine nicht interaktive Shell dazu bringen, sich wie eine interaktive Shell zu verhalten?

Zuerst schauen wir uns an, was passiert, wenn eine neue Shell interaktiv sein muss:

bash -ic 'set -x; [[ !(-z "") ]]; echo $?' 

Es verhält sich wie erwartet, wie Ihre interaktive Shell. Lassen Sie uns jetzt extglobfür eine nicht interaktive Shell aktivieren :

bash -c 'shopt -s extglob; set -x; [[ !(-z "") ]]; echo $?' 

Und es geht nicht! Es verhält sich immer noch wie Ihre nicht interaktive Shell mit extglobdeaktivierter. Die Erklärung ist einfach, aber nicht naheliegend: Das Globbing wird für die gesamte Zeile ausgeführt, bevor shoptes seine Aufgabe erfüllt. Wenn die Option geändert wird, ist es bereits zu spät. Dann teilen wir den Befehl in zwei Zeilen auf:

bash -c 'shopt -s extglob set -x; [[ !(-z "") ]]; echo $?' 

Jetzt verhält es sich wie Ihre interaktive Shell.


Das letzte Rätsel ist folgendes:

+ [[ -n !(-z ) ]] 

Woher -nkommen Sie in Ihrer interaktiven Shell? Meine Hypothese basiert auf der Beobachtung, [[ "random-string" ]]die als behandelt wird [[ -n "random-string" ]](überprüfen Sie es mit set -x). Ich schätze, wenn Sie laufen [[ !(-z "") ]]und extglobaktiviert sind, führt die Tatsache, dass der !( )Operator erkannt, aber nicht erweitert wird, dazu, dass die gesamte !(-z "")Zeichenfolge dort als ein Wort verbleibt, dh Ihr Befehl wird zu etwas

set -x; [[ "!(-z )" ]]; echo $? 

Dieser Befehl (in interaktiver oder nicht interaktiver Shell) verhält sich wie der Befehl, den Sie in der interaktiven Shell ausführen.