HOWTO zur Shell-Kommando-Kombination

(C) 2006-2024 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.

Inhaltsverzeichnis

0) Übersicht
1) Kommandos nacheinander ausführen
2) Hintergrund-Kommandos
3) Pipe
4) Kommando-Substitution
5) UND-Verknüpfung (IFAND)
6) ODER-Verknüpfung (IFOR)
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, bei denen auch zwei (oder mehr) Kommandos
in einer Zeile erlaubt sind.

0) Übersicht   (Toc)

Die Kommando-Kombinationen lassen sich folgendermaßen charakterisieren:

  * Parallel:            Laufen parallel oder seriell/sequenziell ab
  * Abhängig:            Laufen abhängig (synchronisiert) oder unab. voneinander ab
  * Gemeinsam:           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 | VRBT |
  | |            |    JA    |    JA    | NEIN |   JA  |  JA  | NEIN | VRBT |
  | `...` $(...) |   NEIN   |    JA    | NEIN |   JA  |  JA  | NEIN | VRBT |
  | &&           |   NEIN   |    JA    | NEIN |  NEIN |  JA  |  JA  |  JA  |
  | ||           |   NEIN   |    JA    | NEIN |  NEIN |  JA  |  JA  |  JA  |
  | (...)        |   NEIN   |   NEIN   |  JA  |   JA  |  JA  | NEIN | VRBT |
  | {...}        |   NEIN   |   NEIN   |  JA  |  NEIN |  JA  |  JA  |  JA  |
  | <(...)       |   NEIN   |   NEIN   |  --  |   JA  | NEIN | NEIN | VRBT |
  +--------------+----------+----------+------+-------+------+------+------+

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.

1) Kommandos nacheinander ausführen   (Toc)

Zuerst 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 jede Abfrage ein neues Kommando darstellt, ist der Exit-Status anschließend
überschrieben. Soll er mehrfach verwendet werden, ist er zwischenzuspeichern:

  ES="$?"   # Exit Status

2) Hintergrund-Kommandos   (Toc)

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 sofort den Exit-Status "0" (OK) zurück, falls das Kommando
gestartet werden konnte.

Falls ein Kommando nicht gestartet werden konnte, liefert sie Exit-Status 126
(gefunden, aber nicht ausführbar) oder 127 (nicht gefunden) zurück.

3) Pipe   (Toc)

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 "|" bereitgestellten Puffer im
Speicher (etwa 2-16 KByte), indem Kommando CMD1 nur dorthin schreibt, falls
Platz im Puffer vorhanden ist und CMD2 nur daraus liest, falls Daten im Puffer
vorhanden sind:

  CMD1 | CMD2       auch       CMD1 | CMD2 | ... | CMD_N

Der Exit-Status einer Pipeline ist der Exit-Status des LETZTEN Kommandos in der
Pipeline. Bei gesetzter Option -o pipefail der Exit-Status des ERSTEN
Kommandos, bei dem ein Fehler auftritt oder 0 falls bei keinem Kommando ein
Fehler auftritt. Falls ein Kommando in einer Pipeline abbricht, werden alle
Kommandos der Pipeline automatisch beendet.

4) Kommando-Substitution   (Toc)

Kommando CMD2 zuerst ausführen, seine 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 (Bash- und Korn-Shell 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

5) UND-Verknüpfung (IFAND)   (Toc)

Nur dann Kommando CMD2 ausführen, falls Kommando CMD1 erfolgreich ablief
(d.h. einen Exit-Status von "0" = OK ergab):

  CMD1 && CMD2    # Bsp: [ -e FILE ] && rm FILE

Der Exit-Status einer UND-Verknüpfung ist der Exit-Status des LETZTEN von links
nach rechts ausgeführten Kommandos. Beim ERSTEN Kommando mit Exit-Status
"ungleich 0" bricht die Verarbeitung ab.

6) ODER-Verknüpfung (IFOR)   (Toc)

Nur dann Kommando CMD2 ausführen, falls Kommando CMD1 NICHT erfolgreich ablief
(d.h. einen Exit-Status "ungleich 0" = FEHLER ergab):

  CMD1 || CMD2    # Bsp: [ -n "$VAR" ] || VAR=Default

Der Exit-Status einer ODER-Verknüpfung ist der Exit-Status des LETZTEN von
links nach rechts ausgeführten Kommandos. Beim ERSTEN Kommando mit Exit-Status
"gleich 0" bricht die Verarbeitung ab.

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!

7) Subshell   (Toc)

Kommandos CMD1 und CMD2 in einer gemeinsamen SUBSHELL hintereinander starten
und die Ausgaben der Kommandos zusammenfassen (können gemeinsam umgelenkt
werden) oder sie gemeinsam im Hintergrund ausführen:

  ( CMD1; CMD2 )                # Gemeinsam in Subshell starten
  ( CMD1; CMD2 ) > out 2> err   # Gemeinsam Ausgabe umlenken
  ( CMD1; CMD2 ) &              # Gemeinsam in Hintergrund schicken

Der Exit-Status stammt vom letzten in der Klammer ausgeführten Kommando.

ACHTUNG: In den (...) gesetzte Variable haben nach außen KEINE Wirkung.

8) Gruppierung/Codeblock   (Toc)

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 Subshell!

Der Exit-Status stammt vom letzten in der Klammer ausgeführten Kommando.

ACHTUNG: In den {...} gesetzte Variable haben nach außen Wirkung.

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!

9) Temporäre Datei   (Toc)

Die Ergebnisausgabe eines Kommandos als temporäre Datei ablegen und einem
anderen Kommando in Form des 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