1. Index
  2. Shell
  3. C
  4. POSIX
  5. JavaScript

Shell-Programmierung

Die Bourne Again Shell ist ein POSIX-konformer Interpreter für Programme wie dieses:

:(){ :|: & };:

Beispiel

Ein Skript besteht aus einer Folge von Kommandos. Die erste Zeile kann (und sollte) einen speziellen Kommentar (shebang) enthalten, welches den zu verwendenen Interpreter enthält.

hello.sh
#!/usr/bin/env bash if [ $# -eq 0 ] then echo Hello, World\! else for arg do echo Hello, $arg\! done fi

Ausführbar machen und ausführen mit:

chmod +x hello.sh && ./hello.sh
Hello, World!

Argumente übergeben

Ein Kommando besteht aus dem Namen einer Funktion oder eines Programms gefolgt von einer Liste mit Argumenten. In Shellskripten wird die Argumentliste in den Positionsparametern gespeichert. In C-Programmen greift man auf die Argumentliste über den Parameter argv der Funktion main zu.

Programm Option Option Parameter Operand
Kommandozeile mkdir -p -m 755 ~/foo/bar
Positionsparameter $0 $1 $2 $3 $4
Argumentliste in C argv[0] argv[1] argv[2] argv[3] argv[4]

Ein Argument, das mit einem - beginnt, nennt man Option. Manche Optionen haben wiederum Parameter (optarg). Sie steuern üblicherweise das Verhalten des Programms. Das Programm getopts extrahiert alle Optionen aus der Argumentliste. Übrig bleiben die Operanden, mit denen das Programm arbeitet.

Umgebung auslesen

Die Shell verwaltet (wie jeder Prozess) eine Liste von Variablen, die sogenannte Umgebung (environment). Exportierte Variablen vererbt sie an Kindprozesse. Folgende Variablen stehen in jedem Skript zur Verfügung:

0…9
Positionsparamter für Argumente
*
Alle Positionsparamter als Token, wie "$1 $2 …"
@
Alle Positionsparamter als Liste, wie "$1" "$2" …
#
Anzahl der Positionsparamter
?
Exit-Status des letzten Kommandos
-
Flags beim Aufruf der Shell
$
Eigene Prozessnummer
!
Prozessnummer des zuletzt gestarteten Kindprozesses
_
Das letzte übergebene Argument
RANDOM
Pseudo-Zufallszahl (nur Bash)

Variablen substituieren

Eine Variable hat einen Namen und einen Wert. Eine Reihung (array) verhält sich wie eine Streutabelle (hashmap) mit numerischen Schlüsseln. Die Bash erlaubt seit Version 4 auch Zeichenketten als Schlüssel.

null=
path="/foo/bar.txt"
array=(foo bar fnord)
declare -A hash=([foo]=fnord [bar]=snafu)

Das Symbol $ steht für Substitution. Findet die Shell diesen Operator, expandiert sie die entsprechende Variable nach den unten beschriebenen Regeln, bevor sie das Kommando ausführt.

Variable Beispiel Ausgabe Expansion
$Name $path /foo/bar.txt Wert der Variable
${Name} ${path}_ /foo/bar.txt_ Dito
${Name:Pos} ${path:4} bar.txt Ab dem sovielten Zeichen
${Name:Pos:Len} ${path:4:3} bar Ab dem sovielten Zeichen mit Länge
${#Name} ${#path} 12 Länge des Werts
Ersetzung
${Name#Prefix} ${path#*/} foo/bar.txt Präfix abschneiden
${Name##Prefix} ${path##*/} bar.txt Präfix gierig abschneiden (basename)
${Name%Suffix} ${path%/*} /foo Suffix abschneiden (dirname)
${Name%%Suffix} ${path%%/*} Suffix gierig abschneiden
${Name/Glob/Repl} ${path/o/l} /flo/bar.txt Einmal ersetzen
${Name//Glob/Repl}${path//o/u} /fuu/bar.txt Alle ersetzen
${Name/#Glob/Repl}${path/#\/foo/fnord} fnord/bar.txt Vorne ersetzen
${Name/%Glob/Repl}${path/%txt/jpg} /foo/bar.jpg Hinten ersetzen
Null-Variable
${Name:+Text} ${path:+hello} Alternative oder gar nichts.
${Name:-Text} ${path:-hello} /foo/bar.txt Defaultwert
${Name:=Text} ${null:=hello} hello Defaultwert mit Zuweisung an Variable
${Name:?Text} ${null:?hello} hello Meldung auf stderr; beendet die Shell
Variablennamen
${!Prefix*} ${!p*} path Variablennamen mit Präfix als Token
${!Prefix@} ${!p@} path Variablennamen mit Präfix als Liste
Reihung
${Array[*]} ${array[*]} foo bar fnord Werte als Token
${Array[@]} ${array[@]} foo
bar
fnord
Werte als Liste
${!Array[*]} ${!array[*]} 1 2 3 Schlüssel als Token
${!Array[@]} ${!array[@]} 1
2
3
Schlüssel als Liste
${#Array[@]} ${#array[@]} 3 Anzahl der Elemente
${#Array[Key]} ${#array[2]} 5 Länge des Elements
${Array[Key]} ${array[2]} fnord Wert des Elements
Kommando-Substitution
`Command` `uname` Linux Ausgabe des Kommandos, veraltet
$(Command) $(uname) Linux Ausgabe des Kommandos
<(Command) <(uname) Ausgabe von Kommando Umleiten
>(Command) >(uname) Ausgabe zu Kommando Umleiten
Arithmetik
$((Expression)) $((x + 3)) 8 Arithmetische Erweiterung
Wert-Expansion (Klammer, Tilde, Datei)
{Char..Char} foo{a..c} fooa foob fooc Kombinationen mit einer Spanne von Zeichen
{String,…} foo{bar,baz} foobar foobaz Kombinationen mit einer Liste von Zeichenketten
~ ~ /home/bob Benutzerverzeichnis des Prozessbesitzers
~User ~alice /home/alice Benutzerverzeichnis eines anderen Benutzers
* foo* foo foo.txt Beliebige Zeichenkette
? ?oo foo xoo zoo Beliebiges Zeichen
[List] [a-m,x,y]oo foo xoo Ein Zeichen aus einer Gruppe

Ablauf steuern

Command && Command || Command
Ternärer Operator
if [[ Expression ]]; then …; [ elif [[ Expression ]]; then …; else …; ] fi
Verzweigung
case Name in Glob) …;; esac
Mehrfachverzweigung
while [[ Expression ]]; do …; done
Schleife mit Forsetzungsbedingung
until [[ Expression ]]; do …; done
Schleife mit Abbruchbedingung
for Name; do …; done
Iteration über Positionsparameter
for Name in List; do …; done
Iteration über Liste
for ((Expr1; Expr2; Expr3)); do …; done
Iteration wie in C
break
Verlässt die for-, while- oder until-Schleife
continue
Setzt die Schleife beim nächsten Durchlauf fort
Name() { … }
Definiert eine Funktion
return ExitCode
Verlässt die Funktion
trap Command Signal
Registriert Funktion für Signalbehandlung

Logik auswerten

[[ Expression ]]
Operand Operator Operand Wahr, wenn
Lexikographisch
-z String Zeichenkette leer ist
-n String Zeichenkette mindestens ein Zeichen enthält
String = String Zeichenketten identisch sind
String == String Dito
String != String Zeichenketten sich unterscheiden
String < String Linke lexikographisch kleiner als rechte Zeichenkette
String > String Linke lexikographisch größer als rechte Zeichenkette
Arithmetisch
! Expression Negation, Ausdruck ist falsch
Number -eq Number Gleichheit
Number -ne Number Ungleichheit
Number -lt Number Linke Zahl kleiner als rechte Zahl
Number -le Number Linke Zahl kleiner oder gleich rechte Zahl
Number -gt Number Linke Zahl größer als rechte Zahl
Number -ge Number Linke Zahl größer oder gleich rechte Zahl

Arithmetisch rechnen

(( Expression ))
Operand Operator Operand Wahr, wenn
(…) Präzedenz
Number = Number Zuweisung
Number += Number Zuweisung mit Addition
++ Number Increment
-- Number Decrement
~ Number Bitkomplement
Number & Number Bitweise UND verknüpfen
Number ^ Number Bitweise ODER verknüpfen
Number ** Number Exponent
Number * Number Multiplikation
Number / Number Division
Number % Number Modulo
Number + Number Addition
Number - Number Subtraktion

Datenströme umleiten

Eine Pipe verbindet die Standardausgabe eines Prozesses mit der Standardeingabe des Nächsten. Typische Unix-Programme sind dafür ausgelegt, entweder als Quelle (cat, ls), als Filter (grep, sort) oder als Senke (less, lpr) zu agieren.

Source | Filter | Sink
(Standard)eingabe
Command [n]< File Aus Datei lesen
Command [n]<< Delimiter Bis zum Trenner lesen (Here-Dokument)
Command [n]<<- Delimiter Dito, aber Einrückung ignorieren
Command [n]<&[n] Deskriptor duplizieren
Command [n]<&- Deskriptor schließen
(Standard)ausgabe
Command [n]> File In Datei schreiben
Command [n]>| File Datei überschreiben, selbst wenn noclobber gesetzt ist
Command [n]>> File An Datei anhängen
Command [n]>&[n] Deskriptor duplizieren
Command [n]>&- Deskriptor schließen
Command [n]<> File Deskriptor zum Lesen und Schreiben öffnen
0 Standardeingabe
1 Standardausgabe
2 Standardfehlerausgabe

Standardfehlerausgabe im Pager betrachten

Command 2>&1 | less

Der Operator > leitet die Ausgabe von Kommandos und Funktionen in Dateien um.

for i in {1..10}; do echo "Hello $i"; done > File

Umgekehrt leitet < den Inhalt von Dateien in die Standardeingabe um. Um zum Beispiel eine Datei zeilenweise zu lesen:

while read line; do echo $((++n)) $line; done < File

Und so leitet man die Standardausgabe eines Kommandos in die Standardeingabe um:

while read line; do echo $line; done < <(Command)

Umleitung eines Here-Dokuments in ein Kommando:

cat <<- .
the quick brown fox jumps
over the lazy dog
.

Reguläre Ausdrücke

basic extended perl joe
Zeilenanfang ^ ^ ^ \^
Zeilenende $ $ $ \$
Wortanfang \<
Wortende \>
Beliebiges Zeichen . . . \?
Element der Klasse [] [] [] \[]
Kein Element [^] [^] [^] \[^]
Buchstabe [a-zA-Z] [:alpha:] \[a-zA-Z]
Kleinbuchstabe [a-z] [:lower:] \[a-z]
Großbuchstabe [A-Z] [:upper:] \[A-Z]
Ziffer [0-9] [:digit:] \d \[0-9]
Hexadezimalziffer [0-9a-fA-F] [:xdigit:] \[0-9A-Fa-f]
Alphanumerisch [0-9a-zA-Z] [:alnum:] \[0-9a-zA-Z]
Wort-Zeichen [:word:] \w
Leerzeichen [:blank:] \[ \t]
Steuerzeichen [:cntrl:] \[\x00-\x1F\x7F]
Unsichtbares Zeichen [:space:] \s \[ \t\r\n\v\f]
Sichtbares Zeichen [:graph:]
Druckbares Zeichen [:print:]
Interpunktion [:punct:]
Gruppierung \(\) () ()
Verweis auf Gruppe \Num \Num
Alternative | |
0 oder 1 Vorkommen ? ?
Beliebig viele Wdh. * * * \*
Eine oder mehr Wdh. + + \+\[]
Genau Num Wdh. \{Num\} {Num} {Num}
Num bis Num Wdh. \{Num,Num\} {Num,Num} {Num,Num}

Literatur

  1. Robbins, Beebe: Klassische Shell-Programmierung, O'Reilly
  2. Ken Thompson: The UNIX Command Language, Bell Labs, 1976
  3. David Pashley: Writing Robust Bash Shell Scripts
  4. Mendel Cooper: Advanced Bash-Scripting Guide
  5. Joshua Levy: The Art of Command Line
  6. Steve Litt: Perl Regular Expressions