Was ist der Unterschied zwischen && und | im Bash-Skript?


12

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?


@heemayl Danke. Können Sie die Unterschiede in meinem Fall herausarbeiten? Vielen Dank.
Nam G VU

1
Hinweis: Die Befehle auf beiden Seiten |werden in Subshells ausgeführt.
Heemayl

Antworten:


21

In p=$(cd ~ && pwd):

  • Die Befehlsersetzung $()wird in einer Subshell ausgeführt

  • cd ~Wechselt das Verzeichnis in ~(Ihr Zuhause). Wenn dies cderfolgreich ist ( &&), wird pwdder Verzeichnisname auf STDOUT gedruckt. Die Zeichenfolge, in der gespeichert wurde, pist 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 pwdin einem separaten Sub - Shell

  • Sie würden also nur das STDOUT erhalten, pwddh von wo aus Sie den Befehl ausführen. Dies kann ein beliebiges Verzeichnis sein, wie Sie sich vorstellen können. Daher penthält es den Verzeichnisnamen, von dem aus der Befehl aufgerufen wird, und nicht Ihr Ausgangsverzeichnis


Was ist der Zweck der Pipe zwischen den Befehlen? cd ~erzeugt keine Ausgabe und pwdliest keine Eingabe.
Barmar

Der zweite Befehl ist im wesentlichen äquivalent (cd ~);p=$(pwd)ist , nicht wahr?
Barmar

@Barmar Ja, es ist das, was OP verwendet hat, und ich erkläre es nur für ihn.
Heemayl

6

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
~$ 
  • Der Befehl cd ~hat das "aktuelle Verzeichnis" in das Home des Benutzers geändert, der den Befehl ausführt ( /home/user).
  • Nachdem der Befehl erfolgreich ausgeführt wurde (Beendigungscode 0), wird der nächste Befehl nach dem && ausgeführt
  • Und das "aktuelle Arbeitsverzeichnis" wird gedruckt.
  • Die laufende Shell wurde in geändert 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 falseverhindert 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.

  • Das cd ~ändert die pwd einer Shell.
  • Das pwddruckt 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.


5

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 command2nur ausgeführt werden, wenn command1abgeschlossen und ein erfolgreicher Ergebniscode zurückgegeben hat. Wenn dies command1 || command2 command2der Fall ist, wird es ausgeführt, command1wenn command1ein 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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.