Nehmen wir die beiden folgenden Zeilen, um zwei unterschiedliche Ergebnisse zu erhalten.
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
Wie unterscheiden sich die beiden?
|
werden in Subshells ausgeführt.
Nehmen wir die beiden folgenden Zeilen, um zwei unterschiedliche Ergebnisse zu erhalten.
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
Wie unterscheiden sich die beiden?
|
werden in Subshells ausgeführt.
Antworten:
In p=$(cd ~ && pwd)
:
Die Befehlsersetzung $()
wird in einer Subshell ausgeführt
cd ~
Wechselt das Verzeichnis in ~
(Ihr Zuhause). Wenn dies cd
erfolgreich ist ( &&
), wird pwd
der Verzeichnisname auf STDOUT gedruckt. Die Zeichenfolge, in der gespeichert wurde, p
ist also Ihr Zuhause-Verzeichnis, z/home/foobar
In p=$(cd ~ | pwd)
:
$()
Bringt wieder eine Subshell hervor
Die Befehle auf beiden Seiten |
werden in den jeweiligen Subshells ausgeführt (und beide werden gleichzeitig gestartet).
so cd ~
wird in einer Subshell, und fertig pwd
in einem separaten Sub - Shell
Sie würden also nur das STDOUT erhalten, pwd
dh von wo aus Sie den Befehl ausführen. Dies kann ein beliebiges Verzeichnis sein, wie Sie sich vorstellen können. Daher p
enthält es den Verzeichnisnamen, von dem aus der Befehl aufgerufen wird, und nicht Ihr Ausgangsverzeichnis
cd ~
erzeugt keine Ausgabe und pwd
liest keine Eingabe.
(cd ~);p=$(pwd)
ist , nicht wahr?
Das Kernproblem ist, wie die Operatoren &&
und |
die beiden Befehle verbinden.
Das &&
verbindet die Befehle über den Exit-Code. Das |
verbindet die beiden Befehle über die Dateideskriptoren (stdin, stdout).
Vereinfachen wir zuerst. Wir können die Zuordnung aufheben und schreiben:
echo $(cd ~ && pwd)
echo $(cd ~ | pwd)
Wir können sogar die Unter-Shell für die Befehlsausführung entfernen, um dies zu analysieren:
$ cd ~ && pwd
$ cd ~ | pwd
Wenn wir die Eingabeaufforderung ändern, um das Verzeichnis anzuzeigen, in dem die Befehle ausgeführt werden PS1='\w\$ '
, sehen wir etwa Folgendes:
/tmp/user$ cd ~ && pwd
/home/user
~$
cd ~
hat das "aktuelle Verzeichnis" in das Home des Benutzers geändert, der den Befehl ausführt ( /home/user
).pwd
, ~
wie durch die Eingabeaufforderung von angezeigt ~$
.Wenn die Änderung des Verzeichnisses aus irgendeinem Grund nicht erfolgreich war (Exit-Code nicht 0) (Verzeichnis existiert nicht, Berechtigungen blockieren das Lesen des Verzeichnisses), wird der nächste Befehl nicht ausgeführt.
Beispiel:
/tmp/user$ false && pwd
/tmp/user$ _
Der Exit-Code von 1 von false
verhindert die Ausführung des nächsten Befehls.
Somit ist der Beendigungscode von "Befehl 1" derjenige, der den "Befehl 2" beeinflusst.
Nun die Auswirkungen des gesamten Befehls:
/tmp/user$ echo $(cd ~ && pwd)
/home/user
/tmp/user$ _
Das Verzeichnis wurde geändert, aber innerhalb einer Sub-Shell $(…)
wird das geänderte Verzeichnis gedruckt /home/user
, aber sofort verworfen, wenn die Sub-Shell geschlossen wird. Das pwd kehrt zum Ausgangsverzeichnis ( /tmp/user
) zurück.
Das ist, was passiert:
/tmp/user$ cd ~ | pwd
/tmp/user
/tmp/user$ _
Das Metazeichen |
(kein echter Operator) weist die Shell an, ein sogenanntes "Pipe" (in Bash) zu erstellen. Jeder Befehl auf jeder Seite der Pipe ( |
) wird in jede eigene Unterschale gesetzt, zuerst die rechte Seite Befehl, dann der linke. Der Eingabedateideskriptor ( /dev/stdin
) des rechten Befehls wird mit dem Ausgabedeskriptor ( /dev/stdout
) verbunden. Anschließend werden beide Befehle gestartet und können miteinander interagieren. Der linke Befehl ( cd -
) hat keine Ausgabe und der rechte Befehl ( pwd
) akzeptiert auch keine Eingabe. Jeder läuft also unabhängig in jeder eigenen Sub-Shell.
cd ~
ändert die pwd einer Shell.pwd
druckt das (völlig unabhängige) PWD der anderen Unterschale.Die Änderungen an jeder Shell werden verworfen, wenn die Pipe endet. Die externe Sub-Shell hat die PWD nicht geändert.
Deshalb sind die beiden Befehle nur durch "Dateideskriptoren" verbunden.
In diesem Fall wird nichts gesendet und nichts gelesen.
Der ganze Befehl:
$ echo "$(cd ~ | pwd)"
Gibt nur das Verzeichnis aus, in dem der Befehl ausgeführt wurde.
Ich bin mir nicht sicher, ob du '|' oder '||' in deinem zweiten Fall.
'|' In einer Shell wird die Ausgabe eines Befehls an die Eingabe eines anderen weitergeleitet. Ein häufiger Anwendungsfall ist etwa:
curl http://abcd.com/efgh | grep ijkl
Führen Sie einen Befehl aus und verwenden Sie einen anderen Befehl, um die Ausgabe eines Befehls zu verarbeiten.
In dem von Ihnen angegebenen Beispiel ist dies ziemlich unsinnig, da "cd" normalerweise keine Ausgabe generiert und "pwd" keine Eingabe erwartet.
'&&' und '||' sind jedoch Partnerbefehle. Sie sind so konzipiert, dass sie in den meisten Sprachen wie logische Operatoren "und" und "oder" verwendet werden. Die durchgeführten Optimierungen geben ihnen jedoch ein spezifisches Verhalten, das einem Shell-Programmierparadigma entspricht.
Um das Ergebnis einer logischen "und" -Operation zu ermitteln, müssen Sie die zweite Bedingung nur auswerten, wenn die erste Bedingung erfolgreich ist. Wenn die erste Bedingung fehlschlägt, ist das Gesamtergebnis immer falsch.
Um das Ergebnis einer logischen "oder" Operation zu ermitteln, müssen Sie die zweite Bedingung nur auswerten, wenn die erste Bedingung fehlschlägt. Wenn die erste Bedingung erfolgreich ist, ist das Gesamtergebnis immer wahr.
Also, in der Shell, wenn Sie command1 && command2
command2
nur ausgeführt werden, wenn command1
abgeschlossen und ein erfolgreicher Ergebniscode zurückgegeben hat. Wenn dies command1 || command2
command2
der Fall ist, wird es ausgeführt, command1
wenn command1
ein Fehlercode zurückgegeben wird.
Ein weiteres gängiges Paradigma ist, einen Testbefehl zu haben command1
- dies erzeugt eine einfache if / then-Anweisung - zum Beispiel:
[ "$VAR" = "" ] && VAR="Value if empty"
Ist eine (langwierige) Möglichkeit, einer Variablen einen Wert zuzuweisen, wenn sie aktuell leer ist.
Es gibt viele Beispiele für die Verwendung dieses Prozesses an anderer Stelle in Stack Exchange