Text-Terminal
Tastatur
Bildschirm
Programm 1
Programm 2
Programm 3
#0 stdin
#2 stderr
#1 stdout
#0 stdin
#0 stdin
#1 stdout
#2 stderr
#2 stderr
#1 stdout
Technische Praxis der Computersysteme 1
5. Arbeiten mit der Bash
Thomas Leitner <thomas.leitner@univie.ac.at>
Fakultät für Mathematik, Universität Wien Wintersemester 2012
Letzte Änderung: 2013-09-20 20:46:08 +0200
Inhalt
Befehle miteinander kombinieren Ein- und Ausgabe umleiten Auswertungsunterdrückung Expansionen
Wichtige Hilfsprogramme
Befehle kombinieren
Die bash-Shell erlaubt es, mehrere einfache Befehle auf unterschiedeliche Arten miteinander zu komplexen Befehlen zu kombinieren:
Pipelines (umleiten von Ausgabe und Eingabe) Listen (verbinden von Befehlen mit Operatoren) Verbundbefehle
Ein einfacher Befehl besteht aus optionalen Variablenzuweisungen, Ein- und Ausgabeumleitungen und Wörtern. Das erste Wort gibt den auszuführenden Befehl an, die restlichen sind die Argumente. Das sieht z.B. so aus:
$ X=Hallo ruby -e 'puts ENV["X"]' >test
Dabei ist
X=Hallo
eine Variablenzuweisung,ruby
der auszuführende Befehl,-e
undputs ENV["X"]
dieArgumente und
>test
eine Ausgabeumleitung.Am häufigsten werden bei der „normalen“ Arbeit mit der Shell wahrscheinlich die Pipelines verwendet.
Der Rückgabewert der Befehle spielt bei deren Kombination meist eine Rolle.
Pipelines
Pipelines verbinden die Ausgabe (stdout) eines Befehls mit der Eingabe (stdin) eines anderen Befehls.
Das Konzept geht auf die ersten Anfänge der elektronischen Datenverarbeitung zurück und wurde von Douglas McIlroy erfunden.
Die Ausgabe wird unmittelbar weitergegeben und das konkrete Verhalten hängt von den jeweiligen Befehlen ab. Jeder Befehl wird als eigener Prozess ausgeführt, also in einer Subshell.
Die Syntax für eine Pipeline sieht so aus:
[ ! ] command [ [|⎪|&] command2 ... ]
Also ein oder mehrere Befehle, getrennt durch
|
(genannt Pipe, umleiten der Ausgabe) oder|&
(umleiten der Fehlerausgabe).Der Rückgabewert der Pipeline ist der Rückgabewert des letzten Befehls.
Falls am Anfang der Pipeline ein Rufzeichen steht, wird der Rückgabewert logisch negiert.
Pipelines - Beispiele
Alle installierten Benutzer-Befehle auflisten:
$ man -k . | grep '(1)' | less Hier wird
nach all Manpage-Einträgen gesucht, welche mindestens ein beliebiges Zeichen enthalten (
man -k .
);danach werden mit
grep '(1)'
die Zeilen herausgefiltert, die die Zeichenkette ‚(1)‘ beinhalten (also jene Befehle, die in der Manpage-Gruppe 1 sind);die in der Manpage-Gruppe 1 sind);
und zum Schluss wird das Ergebnis mit dem Pager
less
seitenweise dargestellt.Zählen der Dateien in einem Verzeichnis:
$ ls -l /usr/include/ | wc -l 231
Listen
Eine Liste ist eine Folge einer oder mehrerer Pipelines, separiert durch einen der Operatoren
; & && ||
und optional terminiert durch;
,&
oder einem Zeilenumbruch.Befehl1 [ [ ; ⎪ & ⎪ && ⎪ || ] Befehl2 ... ] [ ; | & ]
Die Operatoren
&&
und||
haben gleichen Vorrang, gefolgt von;
und&
, die auch gleichen Vorrang haben.Operator
;
: Die Befehle werden hintereinander ausgeführt. Der Rückgabewert ist der Rückgabewert des letzten Befehls.Operator
&
: Der vorangegange Befehl wird im „Hintergrund“ ausgeführt, der Rückgabewert ist 0.Operator
&&
: Befehl2 wird nur dann ausgeführt, wenn Befehl1 den Rückgabewert 0 hat. Der Rückgabewert ist der Rückgabewert des letzten, ausgeführten Befehls.Operator
||
: Befehl2 wird nur dann ausgeführt, wenn Befehl1 einen Rückgabewert ungleich 0 hat. Der Rückgabewert ist der Rückgabewert des letzten, ausgeführten Befehls.Listen - Beispiele
Verzeichnis erstellen und sofort etwas hineinkopieren:
$ mkdir foo && cp source foo/
Unter Ubuntu die Paketlisten aktualisieren und alle neuen Pakete einspielen:
$ apt-get update && apt-get upgrade
Eine Datei löschen und bei einem Fehler eine entsprechende Nachricht ausgeben:
$ rm datei || echo Das Löschen ist fehlgeschlagen.
Kombination mehrerer Operatoren:
$ echo sofort | cat; sleep 1 && echo zum schluss & echo in der mitte sofort
[1] 15291 in der mitte
$ zum schluss
Verbundbefehle
(Liste)
: Die Liste wird in einem eigenen Shell-Prozess ausgeführt (d.h. unter anderem, dass gesetzte Variablen die aktuelle Shell nicht beeinflussen). Der Rückgabewert ist der Rückgabewert der Liste.{ Liste; }
: Die Liste wird in der aktuellen Shell ausgeführt. Wichtig: Die Liste muss mit einem Semikolon oder einem Zeilumbruch terminiert werden und die geschwungenen Klammern müssen mit Leerzeichen von der Liste separiert sein!Der Rückgabewert ist der Rückgabewert der Liste.
((Ausdruck))
: Der Ausdruck stellt eine Rechnung dar und diese Rechnung wird von der Bash ausgeführt. Wenn das Ergebnis der Rechnung 0 ist, ist der Rückgabewert 1 und sonst 0. Mehr Informationen in der Manpage von bash unter„ARITHMETIC EVALUATION“.
[[ Ausdruck ]]
: Der logische Ausdruck wird evaluiert und der Rückgabewert ist das Ergebnis der Evaluation. Mehr Informationen in der Manpage von bash unter „CONDITIONAL EXPRESSION“.Verbundbefehle - Beispiele
Liste in eigenem Shell-Prozess:
$ X=5; (echo -n $X; X=7; echo -n $X); echo $X 575
Liste im aktuellen Shell-Prozess:
$ X=5; {echo -n $X; X=7; echo -n $X}; echo $X {echo: command not found
7}7
$ X=5; { echo -n $X; X=7; echo -n $X; }; echo $X 577
Rückgabewert von Berechnungen verwenden:
$ X=5 && (( X - 5 == 0 )) && echo yes || echo no yes
$ X=6 && (( X - 5 == 0 )) && echo yes || echo no no
Rückgabewert von logischen Ausdrücken verwenden:
$ X=hallo && [[ $X = hallo ]] && echo yes || echo no yes
$ X=hallo && [[ $X = 'nicht hallo' ]] && echo yes || echo no no
Ein- und Ausgabeumleitung
Mit der Ein- und Ausgabeumleitung ist es möglich, als Eingabe für einen Befehl den Inhalt einer Datei zu verwenden oder die Ausgabe in eine Datei zu schreiben.
Die Umleitungsangaben können bei einem einfachen Befehl irgendwo stehen, bei einem komplexen Befehl nur danach.
<Datei
: Die angegebene Datei wird als Standardeingabe (stdin) verwendet.>Datei
: Die angegebene Datei wird als Standardausgabe (*stdout) verwendet. Falls die Datei noch nicht existiert, wird sie angelegt. Falls sie existiert, wird der Inhalt zuvor gelöscht.>>Datei
: Die angegebene Datei wird als Standardausgabe verwendet, aber der Inhalt der Datei wird dabei nicht gelöscht (d.h. die Ausgabe wird hinten angehängt).Mit Hilfe von
n<&Wort
bzw.n>&Wort
können Eingabe- bzw. Ausgabedatenströme dupliziert werden. Zum Beispiel bedeutet2>&1
, dass die Fehlerausgabe das selbe Ziel haben soll wie die Standardausgabe.Mehr Information in der Manpage von bash unter „REDIRECTION“ bzw. im Kapitel I/O Redirection des Advanced Bash-Scripting Guide.
Ein- und Ausgabeumleitung - Beispiele
Dateien suchen und die gefundenen Dateien für die spätere Bearbeitung in eine Datei schreiben:
$ find /usr/include -printf %P\\n > found_files
$ find /usr/src/linux-headers-3.2.0-25/include/ -printf %P\\n >> found_files Eine Pipeline mit Daten einer Datei füttern:
$ ( grep /usb/ | sort | uniq | wc -l ) < found_files 48
Die Ausgabe und/oder die Fehlerausgabe ignorieren:
$ find /usr/include non_existing_directory 2>&1 >/dev/null find: `non_existing_directory': No such file or directory
$ find /usr/include non_existing_directory >/dev/null 2>&1
$ find /usr/include non_existing_directory &>/dev/null
Zusatz: Die Standardeingabe in der aktuellen Shell schließen (äquivalent zum Befehl
exit
):$ exec 0<&-
Named Pipes (FIFO)
Wir haben bei den Linux-Dateitypen unter anderem den speziellen Typ Named Pipe kennengelernt. Dateien dieses Typs funktionieren nach dem First in, first out-Prinzip (so wie die Pipelines), d.h. alles was „auf der einen Seite“ hineingeschrieben wird, kommt sofort ohne Zwischenspeicherung „auf der anderen Seite“ heraus.
Named Pipes werden oft benutzt, um Hintergrundprozessen, die schon beim Systemstart geladen worden sind, Daten zu schicken.
Solange nicht beide Seiten, also sowohl die Eingabeseite als auch die Ausgabeseite, einer Named Pipe benutzt werden, blockiert der Kernel den Datentransfer. Die Reihenfolge der Befehle spielt dabei keine Rolle.
Named Pipes (FIFO) - Befehle (mkfifo)
mkfifo - Erstellt eine Named Pipe
Auswertungsunterdrückung (Quoting)
Die Auswertungsunterdrückung (Quoting) erlaubt es, die spezielle Bedeutung von Zeichen oder Wörtern zu unterdrückung.
Spezielle Bedeutung haben z.B. die Zeichen
| & ; ( ) < > space tab
und Wörter der Form$WORT
.Drei Möglichkeiten:
Maskierungszeichen
\
: Das Zeichen nach dem Maskierungszeichen behält seine ursprüngliche Bedeutung bei.Beispiele:
\\
→ Zeichen\
\n
→ Zeilenumbruch\r
→ Cursor zurück zum Zeilenanfang bewegen\
→ Leerzeichen (z.B. in Dateinamen)'...'
: Starkes Quoting; alles innerhalb von einfachen Anführungszeichen bleibt genau so erhalten."..."
: Schwaches Quoting; die Zeichen$
,`
,!
und\
behalten ihre spezielle Bedeutung (\
nur für$ ` ! "
),alle anderen Zeichen bleiben erhalten.
Auswertungsunterdrückung - Beispiele
$ VAR=Hallo
$ echo "$VAR\n$VAR"
hallo\nhallo
$ echo -e $VAR\\n$VAR hallo
hallo
$ echo '$VAR'
$VAR
$ echo -e "Hallo\rHe"
Hello
Expansion
Die bash unterstützt mehrere Arten von Expansionen, wir werden einen kurzen Blick auf die Klammernexpansion, Pfadexpansion, die Variablenexpansion und die Befehlssubstitution werfen.
Diese Expansionen werden vor dem Aufruf des eingegebenen Befehls in einer vordefinierten Reihenfolge durchgeführt. Die Expansionen, die wir betrachten, werden in der Reihenfolge
Klammernexpansion, Variablenexpansion, Befehlssubstitution und Pfadexpansion durchgeführt.
Genaue Informationen zu diesem Thema findet man in der Manpage von bash unter ‚EXPANSION‘.
Klammernexpansion
Die Klammernexpansion (brace expansion) erlaubt das Generieren beliebiger Zeichenfolgen. Dabei werden keine speziellen Zeichen anderer Expansionen interpretiert, der Mechanismus basiert rein auf Textbasis.
Ein Muster für die Klammernexpansion besteht aus einem optionalen Präfix, einer Menge von entweder zwei oder mehr Parameter ist der Name der zu erstellenden Datei
$ mkfifo test; ls -l test
prw-rw-r-- 1 thomas thomas 0 Oct 23 19:37 test
$ echo hallo > test &
[1] 21452
$ cat <test hallo
[1]+ Done echo hallo > test
$ tail -f test | sed 's/NAME/Tux/g' &
[1] 21582
$ echo Hallo, ich bin NAME. > test Hallo, ich bin Tux.
$ echo NAME ist ein schöner Name, NAME, NAME, NAME, ... > test Tux ist ein schöner Name, Tux, Tux, Tux, ...
»
»
Zeichenfolgen, die mit Beistrichen getrennt in geschwungenen Klammern stehen, oder einer Sequenz, und einem Suffix.
Muster können auch geschachtelt werden. Sequenzen haben die Form
x..y[..Inkrement]
.Also:
Präfix{Zeichen1, Zeichen2, Zeichen3}Suffix
Beispiele:
$ echo a{d,b,c}e ade abe ace
$ echo a{00..10..2}
a00 a02 a04 a06 a08 a10
$ echo a{b,c{1..3},d}z abz ac1z ac2z ac3z adz
Pfadexpansion
Bei der Pfadexpansion (pathname expansion) wird ein Muster mit passenden Dateinamen ersetzt. Das erlaubt die schnelle und einfache Behandlung von vielen Dateien auf einmal.
Ein Muster kann folgende, spezielle Zeichen beinhalten:
*
: Passt auf jeden Dateinamen.?
: Passt auf genau ein Zeichen.[...]
: Passt auf genau die Zeichen in den Klammern. Mit einem Bindestrich kann man Bereiche von Zeichen angeben (z.B.a-z
).Beispiele:
$ echo folien_*
folien_00.page folien_01.page folien_02.page folien_03.page folien_04.page folien_05.page
$ echo *.template
folien-print.template folien.template titelfolie.template
$ echo folien_?[3-5]*
folien_03.page folien_04.page folien_05.page
Variablenexpansion
Bei der Variablenexpansion wird der Wert einer Variablen, eventuell verändert, zurückgegeben.
Die wichtigsten Varianten sind:
${Variable}
: Einfach den Wert zurückgeben.${Variable:Offset[:Länge]}
: Alle (oder maximal ‚Länge‘ Zeichen) Zeichen ab Offset (0-basierend) zurückgeben.${Variable#Präfix}
: Das Präfix vom Wert löschen, falls der Wert mit dem Präfix beginnt. Das Präfix wird wie bei der Pfadexpansion behandelt. Mit zwei#
wird das längste (sonst das kürzeste) Präfix gelöscht.${Variable%Suffix}
: Das Suffix vom Wert löschen, falls der Wert mit dem Suffix endet. Das Suffix wird wie bei der Pfadexpansion behandelt. Mit zwei%
wird das längste (sonst das kürzeste) Suffix gelöscht.${Variable/Muster/Zeichenfolge}
: Das Muster (expandiert wie bei der Pfadexpansion) wird durch die Zeichenfolge ersetzt.Variablenexpansion - Beispiele
Beispiele:
VAR=TechnischePraxis
$ echo ${VAR}
TechnischePraxis
$ echo ${VAR:10}
Praxis
$ echo ${VAR:10:3}
Pra
$ echo ${VAR#*i}
schePraxis
$ echo ${VAR##*i}
s
$ echo ${VAR%i*}
TechnischePrax
$ echo ${VAR%%i*}
Techn
$ echo ${VAR/T*he/Andere}
AnderePraxis
Befehlssubstitution
Die Befehlssubstitution erlaubt das Ersetzen eines Befehls mit dessen Ausgabe.
Es gibt zwei Formen:
`Befehl`
oder$(Befehl)
. Bei komplizierteren Befehlen ist die zweite Form besser, da der Befehl so behandelt wird, als würde er alleine stehen.Beispiele:
$ VAR=$(echo a b); echo $VAR a b
$ cat $(echo a b c)
cat: a: No such file or directory cat: b: No such file or directory cat: c: No such file or directory
$ cat "$(echo a b c)"
cat: a b c: No such file or directory
Bearbeiten von Pfaden (basename, dirname)
Einen Pfad in Verzeichnis, Dateiname und Dateierweiterung aufsplitten:
$ FILE=/usr/include/fstab.h
$ DIRNAME=$(dirname "$FILE"); BASENAME=$(basename "$FILE")
$ FILENAME="${BASENAME%.*}"; EXTNAME="${BASENAME##*.}"
$ echo "$FILE $DIRNAME $FILENAME $EXTNAME"
/usr/include/fstab.h /usr/include fstab h
Zeilen selektieren (head, tail)
Beide Befehle kombinieren um einen Bereich von Zeilen zu wählen:
$ tail -n +5 /etc/passwd | head -n 2 # Zeilen 5 und 6 sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
Zeilen sortieren (sort)
basename - Entfernt die führenden Verzeichnisse und optional ein Dateisuffix.
Parameter sind der Pfad und optional das Suffix.
$ basename /usr/include/
include
$ basename /usr/include/fstab.h; basename /usr/include/fstab.h .h fstab.h
fstab
dirname - Entfernt die letzte Komponente vom Pfad.
Parameter ist der Pfad.
$ dirname /usr/include/
/usr
$ dirname /usr/include/fstab.h /usr/include
»
»
»
»
head - Gibt nur den Anfang einer oder mehrerer Dateien aus.
Optionen:
-n [-]K
→ nur die ersten K Zeilen ausgeben, bzw. bei -K alle bis auf die letzten K Zeilen (default 10).$ head -n 2 /etc/passwd
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh tail - Gibt nur das Ende einer oder mehrerer Dateien aus.
Optionen:
-n [+]K
→ nur die letzten K Zeilen ausgeben, bzw. bei +K alle Zeilen ab der K-ten Zeile (default 10);-f
→neue Zeilen ausgeben, sobald sie in der Datei erscheinen.
$ tail -n 2 /etc/passwd
ntp:x:120:132::/home/ntp:/bin/false
sshd:x:121:65534::/var/run/sshd:/usr/sbin/nologin
»
»
»
»
sort - Sortiert die Zeilen einer oder mehrerer Dateien.
Standardmäßig wird alphabetisch sortiert, vom Zeilenanfang weg.
»
Teile einer Zeile auswählen (cut)
Sonstige Befehle für Textdateien (uniq)
Sonstige Befehle für Textdateien (nl, wc) -n
→ numerisch sortieren-h
→ menschenlesbare Zahlen vergleichen (1G, 10K, …)-r
→ Sortierrichtung umdrehen-u
→ gleiche Zeilen nur einmal ausgeben-t Zeichen
→ Feldtrennzeichen (default: Übergang von nicht-leer auf leer)-k POS1[,POS2]
→ Felder POS1 bis POS2 (oder bis Zeilenende) verwenden$ sort /etc/passwd | head -n 3
avahi-autoipd:x:105:112:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false avahi:x:106:113:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
backup:x:34:34:backup:/var/backups:/bin/sh
$ sort -t : -k 3 /etc/passwd | head -n 3 root:x:0:0:root:/root:/bin/bash
libuuid:x:100:101::/var/lib/libuuid:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
$ sort -n -t : -k 3 /etc/passwd | head -n 3 root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh
»
»
cut - Gibt nur bestimmte Teile einer Zeile aus.
Nützliche Optionen:
-d Zeichen
→ Trennzeichen (default ist Tabulator)-f Liste
→ nur die angegebenen Felder--output-delimiter Zeichen
→ Trennzeichen für Ausgabe-c Liste
/-b Liste
→ nur die angegebenen Zeichen/Bytes Die Liste ist eine Folge von Bereichen, separiert mit Beistrichen:N
→ N-tes Byte, Zeichen oder Feld, beginnend bei 1N-
→ ab N-tem Byte, Zeichen oder FeldN-M
→ vom N-ten bis zum M-ten Byte, Zeichen oder Feld-M
→ vom ersten bis zum M-ten Byte, Zeichen oder Feld$ cut -d: -f 1-3,1 /etc/passwd | head -1 root:x:0
$ cut -d: -f 1-3,1 --output-delimiter=- /etc/passwd | head -1 root-x-0
$ cut -c 1-4,6 /etc/passwd | head -1 rootx
»
»
uniq - Zeigt wiederholte Zeilen an oder lässt sie weg.
Syntax:
uniq [OPTION]... [Eingabe [Ausgabe]]
.Ohne Optionen werden wiederholte Zeilen nur einmal ausgeben.
Optionen:
-u
→ nur einzigartige Zeilen ausgeben,-d
→ nur wiederholte Zeilen ausgeben,-f N
→ die ersten N Felder ignorieren (Felder werden von Leerzeichen/Tabulatoren getrennt)$ cut -d: -f1,7 --output-delimiter ' ' /etc/passwd | sort -k 2 | uniq -f 1 -c 2 root /bin/bash
19 avahi-autoipd /bin/false 18 backup /bin/sh
1 sync /bin/sync
1 sshd /usr/sbin/nologin
$ cut -d: -f1,7 --output-delimiter ' ' /etc/passwd | sort | uniq -u 1 sync /bin/sync
1 sshd /usr/sbin/nologin
»
»
»
»
nl - Nummeriert Zeilen.
Es gibt viele Optionen, wie die Zeilen nummeriert werden sollen → Manpage!
$ nl /etc/passwd | tail -n +5 | head -n 2 5 sync:x:4:65534:sync:/bin:/bin/sync 6 games:x:5:60:games:/usr/games:/bin/sh wc - Zählt Zeilen, Wörter, Zeichen und Bytes einer oder mehrerer Dateien.
»
»
Copyright und Lizenz
Copyright: Thomas Leitner thomas.leitner@univie.ac.at
Basiert teilweise auf den Folien von Harald Schilly harald.schilly@univie.ac.at Lizenz: Creative Commons CC BY-NC-SA
„Namensnennung-Keine kommerzielle Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Österreich.“ - http://creativecommons.org/licenses/by-nc-sa/3.0/at/
Standardmäßig wird die Anzahl der Zeilen, Wörter und Bytes ausgegeben. Die Ausgabenreihenfolge ist immer: Zeilen, Wörter, Zeichen, Bytes.
Optionen:
-l
→ Anzahl der Zeilen ausgeben,-w
→ Anzahl der Wörter ausgeben,-c
→ Anzahl der Bytes ausgeben,-m
→ Anzahl der Zeichen ausgeben
$ wc notizen.org
22 111 755 notizen.org
$ wc -c -m notizen.org 749 755 notizen.org
»
»
»