HOWTO zur Shell-Kommando-Kombination
(C) 2006-2017 T.Birnthaler/H.Gottschalk <howtos(at)ostc.de>
OSTC Open Source Training and Consulting GmbH
www.ostc.de
$Id: shell-command-combination-HOWTO.txt,v 1.16 2019/11/26 19:37:07 tsbirn Exp $
Dieses Dokument beschreibt die verschiedenen Verfahren zur Kombination von
Kommandos in der Shell.
0) Übersicht
1) Kommandos nacheinander ausführen
2) Hintergrund-Kommandos
3) Pipe
4) Kommando-Substitution
5) UND-Verknüpfung
6) ODER-Verknüpfung
7) Subshell
8) Gruppierung/Codeblock
9) Temporäre Datei
Shells (sh, bash, ksh, zsh, csh, tcsh, ...) erlauben nur EIN Kommando pro
Zeile, jedes Kommando muss durch einen Zeilenumbruch abgeschlossen werden
(oder durch einen ";"). Allerdings gibt es eine Vielzahl von
Kommando-Kombinationen mit Hilfe bestimmter Sonderzeichen, durch die auch zwei
(oder mehr) Kommandos in einer Zeile erlaubt sind.
Die Kommando-Kombinationen lassen sich folgendermaßen charakterisieren:
* Kommandos: Laufen parallel oder sequentiell ab
* Kommandos: Laufen abhängig oder unabhängig voneinander ab
* Kommandos: Gemeinsam umlenkbar/in Hintergrund stellbar oder nicht
* Subshell: Zur Ausführung zusätzlich gestartet oder nicht
* Exit-Status: Abfragbar oder nicht
* Shell-Variablen: Gemeinsam oder nicht (nur Shell-Kommandos)
* Umgebungs-Variablen: Gemeinsam oder nicht (Environment)
(Environment-Var.) (an Kindprozess immer vererbt, nicht zurückgegeben)
+--------------+----------+----------+------+-------+------+------+------+
| Kombination | Parallel | Abhängig | Gem. | Subsh | Exit | ShVar|EnvVar|
+--------------+----------+----------+------+-------+------+------+------+
| NEWLINE ; | NEIN | NEIN | NEIN | NEIN | JA | JA | JA |
| & | JA | NEIN | NEIN | JA | NEIN | NEIN | NEIN |
| | | JA | JA | NEIN | JA | JA | NEIN | NEIN |
| `...` $(...) | NEIN | JA | NEIN | JA | JA | NEIN | NEIN |
| && | NEIN | JA | NEIN | NEIN | JA | JA | JA |
| || | NEIN | JA | NEIN | NEIN | JA | JA | JA |
| (...) | NEIN | NEIN | JA | JA | JA | NEIN | NEIN |
| {...} | NEIN | NEIN | JA | NEIN | JA | JA | JA |
| <(...) | NEIN | NEIN | -- | JA | NEIN | NEIN | NEIN |
+--------------+----------+----------+------+-------+------+------+------+
HINWEIS: Nach Pipe-Symbol "|", logischer Verknüpfung "&&" und "||" sowie nach
"{" darf ein Zeilenumbruch folgen. Ebenso darf innerhalb der Hochkommas "..."
und '...' sowie `...` und der Klammern "(...)" und "{...}" beliebig
Zeilenumbruch verwendet werden.
Erst CMD1 ausführen und nach dessen Abschluß CMD2 ausführen,
die Kommandos sind NICHT miteinander verknüpft:
CMD1 oder CMD1; CMD2
CMD2
Direkt nach jedem Kommando kann sein Exit-Status abgefragt werden:
CMD1 oder CMD1; echo "$?"
echo "$?"
Da die Abfrage ein neues Kommando darstellt, ist der Exit-Status anschließend
überschrieben. Soll er mehrfach verwendet werden, ist er zwischenzuspeichern:
ES="$?"
Kommandos GLEICHZEITIG (parallel) im Hintergrund ausführen, die Kommandos sind
NICHT miteinander verknüpft:
CMD1 & oder CMD1 & CMD2 &
CMD2 &
Der Exit-Status eines Hintergrund-Kommandos kann NICHT abgefragt werden. Die
Shell liefert immer Exit-Status 0 (OK) zurück, falls das Kommando gestartet
werden konnte. Falls das Kommando nicht gestartet werden konnten, liefert sie
Fehlerstatus 126 oder 127 zurück.
Kommandos GLEICHZEITIG (parallel) starten und die Standard-Ausgabe von Kommando
CMD1 an die Standard-Eingabe von Kommando CMD2 übergeben. Die beiden Prozesse
synchronisieren sich über den von der Pipe "|" bereit gestellten Puffer im
Speicher (etwa 2-16 KByte), indem Kommando CMD1 nur dorthin schreibt, wenn
Platz im Puffer vorhanden ist und CMD2 nur daraus liest, wenn Daten im Puffer
vorhanden sind:
CMD1 | CMD2 auch CMD1 | CMD2 | ... | CMD_N
Der Exit-Status der Pipeline ist der Exit-Status des LETZTEN Kommandos. Falls
ein Kommando mit Fehler abbricht, werden alle Kommandos der Pipeline beendet.
Zuerst das Kommando CMD2 ausführen, seine Ausgabe auf der Standard-Ausgabe
in die Kommandozeile von CMD1 einfügen und dann Kommando CMD1 aufrufen:
CMD1 `CMD2` # Alte Form (Bourne-Shell sh)
CMD1 $(CMD2) # Moderne Form (nur bash und ksh)
Nur der Exit-Status von CMD1 kann abgefragt werden. Um den Exit-Status von
CMD2 und CMD1 abzufragen, folgende Konstruktion benutzen:
RESULT=$(CMD2) # Ausgabe von CMD2 abfangen
echo "$?" # Exit-Status von CMD2
CMD1 "$RESULT" # Ausgabe von CMD2 an CMD1 übergeben
echo "$?" # Exit-Status von CMD1
Nur dann Kommando CMD2 ausführen, wenn Kommando CMD1 erfolgreich ablief
(d.h. einen Exit-Status von "0" = Ok ergab):
CMD1 && CMD2 # Bsp: [ -e FILE ] && rm FILE
Exit-Status ist der Exit-Status des LETZTEN von links nach rechts ausgeführten
Kommandos (ein Exit-Status ungleich 0 bricht die Verarbeitung ab).
Nur dann Kommando CMD2 ausführen, wenn Kommando CMD1 NICHT erfolgreich ablief
(d.h. einen Exit-Status ungleich "0" = Fehler ergab):
CMD1 || CMD2 # Bsp: [ -n "$VAR" ] || VAR=Default
HINWEIS: && und || NICHT mischen, da die Bedeutung derartiger Verknüpfungen
sehr schwer verständlich ist (obwohl sie klar festgelegt ist):
CMD1 && CMD2 || CMD3 # CMD1 Fehler? -> CMD2+CMD3 nicht ausführen
# CMD1 Ok? -> CMD2 Ok? -> CMD3 nicht ausführen!
# CMD1 Ok? -> CMD2 Fehler? -> CMD3 ausführen!
CMD1 || CMD2 && CMD3 # CMD1 Ok? -> CMD2+CMD3 nicht ausführen
# CMD1 Fehler? -> CMD2 Fehler? -> CMD3 nicht ausführen!
# CMD1 Fehler? -> CMD2 Ok? -> CMD3 ausführen!
Exit-Status ist der Exit-Status des LETZTEN von links nach rechts ausgeführten
Kommandos (ein Exit-Status gleich 0 bricht die Verarbeitung ab).
Kommandos CMD1 und CMD2 in einer gemeinsamen SUBSHELL hintereinander starten,
die Ausgaben können gemeinsam umgelenkt werden:
( CMD1; CMD2 ) # Gemeinsam in Subshell starten
( CMD1; CMD2 ) > out 2> err # Gemeinsam Ausgabe umlenken
( CMD1; CMD2 ) & # Gemeinsam in Hintergrund schicken
Exit-Status ist der Exit-Status des letzten in der Klammer ausgeführten
Kommandos.
Kommandos CMD1 und CMD2 in der aktuellen Shell hintereinander starten und die
Ausgaben der Kommandos zusammenfassen (können gemeinsam umgelenkt werden) oder
sie gemeinsam im Hintergrund ausführen.
{ CMD1; CMD2; } # Analog CMD1; CMD2; ohne {...}
{ CMD1; CMD2; } > out 2> err # Gemeinsam Ausgabe umlenken (in akt. Shell)
{ CMD1; CMD2; } & # Im Hintergrund: Erzeugt trotzdem eine Subshell
Exit-Status ist der Exit-Status des letzten in der Klammer ausgeführten
Kommandos.
ACHTUNG: Ein Strichpunkt ";" ist auch nach dem letzten Kommando notwendig und die
Leerzeichen nach "{" und vor "}" sind ebenfalls notwendig!
HINWEIS: Geschweifte Klammern "{" und "}" sind aus historischen Gründen
Shell-Schlüsselworte --> Werden nur als 1. Symbol einer Zeile oder nach ";"
erkannt und Leerzeichen davor/dahinter sind notwendig!
Die Ergebnisausgabe eines Kommandos als temporäre Datei ablegen und einem
anderen Kommando in Form eines Dateinamens übergeben:
CMD1 <(CMD2)
Nur der Exit-Status von CMD1 kann abgefragt werden. Um den Exit-Status von
CMD2 und CMD1 abzufragen, folgende Konstruktion benutzen:
RESULT=$(CMD2) # Ausgabe von CMD2 abfangen
echo "$?" # Exit-Status von CMD2
echo "$RESULT" | CMD1 # Ausgabe von CMD2 an CMD1 übergeben
echo "$?" # Exit-Status von CMD1