Es gibt unzählige Tutorials, die verschiedene Befehle beschreiben, die besondere Aufgaben erledigen. Hier stelle ich einige Dinge vor, die nicht auf besonderen Befehlen beruhen, sondern eine Methode oder Syntax beschreiben. Wenn nicht anders angegeben, sind die Beispiele an der Bash-Konsole lauffähig.
Warnungen und Fehler ignorieren
Gelegentlich gibt es Befehle, die sehr „gesprächig“ sind und ihre Ausgabe mit Warnungen und Hinweisen ergänzen, die nicht immer nützlich sind und die Lesbarkeit senken. Die meisten Befehle schreiben solche Ergänzungen nicht in die Standardausgabe, sondern in eine spezielle Fehlerausgabe, die aber standardmässig auf die Standardausgabe umgeleitet wird. Das kann man aber ändern. Und als Ziel dieser unerwünschten Hinweise bietet sich /dev/null (das „Null-Gerät“) an, welches jegliche Ausgabe aufnimmt und dann einfach ignoriert oder anschaulich gesagt “verschluckt”. Der Fehlerkanal hat die Nummer 2 und die Umleitung erfolgt wie vielleicht schon bekannt mit dem Größerzeichen:
# frage die TLS-Daten vom Blog-Server ab und gebe nur den Gültigkeitszeitraum aus echo | openssl s_client -connect dd3ah.de:443 2>/dev/null | openssl x509 -noout -dates
Parameter, die auch eine Variable oder eine Option sein können
Hat man eine Datei erzeugt, die mit einem Bindestrich beginnt, so ist es schwer sie anzuzeigen, löschen oder umbenennen zu können, weil die meisten Befehle den Bindestrich als Option lesen. Entweder wird die Option ungewollt aktiviert oder es gibt einen Fehler. Jedenfalls wird man so nicht die ungewöhnlich benannte Datei ansprechen können. Ähnliches gilt, wenn der Name ein Dollar-Zeichen enthält. Dieses wird als Variable interpretiert. Hier helfen zwei Dinge: Die einfachen Anführungszeichen verhindern, dass die Variable ausgewertet wird und der doppelte Bindestrich signalisiert dem Befehl, dass danach keine weiteren Optionen mehr kommen und weitere Bindestriche nicht als Option interpretiert werden sollen. Auch Leerzeichen können so „eingefangen“ werden.
# erzeuge merkwürdige Datei touch -- '-a $b' # zeige merkwürdige Datei an ls -l -- '-a $b' # lösche merkwürdige Datei rm -- '-a $b'
Variablen gezielt ansprechen
Möchte man umgekehrt Variablen explizit ansprechen, obwohl sie mitten im Text stehen, kann man den Namen in geschweifte Klammern schreiben. Und man kann sie in doppelte Anführungszeichen setzen um zum einen deutlich zu machen, dass hier Variablen angesprochen werden und zum anderen auch Leerzeichen zu benutzen.
# erzeuge Variablen a=123 b=456 # erzeuge Dateien aus diesen Variablen touch "a${a}a b${b}b" touch "b${b}b a${a}a" # Liste Dateien in der Reihenfolge ihrer Erzeugung auf ls -ltr
Sonderzeichen erzeugen
Gelegentlich braucht man Zeichen, die man nicht direkt eintippen kann. Hier hilft der Backslash. So lassen sich das Tabulatorzeichen und der Zeilenrücklauf mit t und n erzeugen. Wichtig ist dabei zu beachten, dass diese Expansion („Erweiterung“) der sogenannten Escape-Sequenzen nicht überall gleich funktioniert. Die Bash selbst beherrscht etwa die Ergänzung von Dateinamen mit Leerzeichen bei der Completion („Vervollständigung“) mittels der Tabulatortaste. Man muss dabei zwischen zwei Fällen unterscheiden:
- Die Escape-Sequenz dient dazu, Zeichen zu erzeugen, die sonst eine andere syntaktische Bedeutung hätten, wie Klammern, Anführungszeichen, Leerzeichen. Das ist die Methode der Bash.
- Die Escape-Sequenz führt Zeichen ein, die (nicht so einfach) einzugeben wären, wie Tabulatoren und Zeilenumbrüche.
Hier ein Beispiel mit dem Befehl sed für den zweiten Fall.
# tausche das Hash gegen einen Tabulator echo '123#345' | sed 's/#/\t/;'
Ausgabe eines Befehls wie eine Variable nutzen
Um die Ausgabe eines Befehls in die Parameter eines anderen Befehls zu integrieren, kann das Dollar-Zeichen mit runden Klammern genutzt werden. Alternativ geht auch ein Akzentzeichen, welches aber leicht mit den einfachen Anführungszeichen verwechselt werden kann.
# Gebe das Datum mit einem zusätzlichen Text aus echo Heute haben wir das Datum: $(date)
Erfolg eines Befehls auswerten
Manchmal möchte man die Ausgabe eines Befehls nicht im Detail, sondern nur in Abhängigkeit, ob es überhaupt geklappt hat, etwas anderes machen. Den Erfolg kann man mit zwei Ampersand auswerten, den Misserfolg mit zwei Pipe-Symbolen.
# prüfe ob die Datei /etc/hosts einen Eintrag für 127.0.0.1 enthält grep -q '^127\.0\.0\.1' /etc/hosts && echo Eintrag für localhost enthalten # versuche als normaler User eine Datei an der Wurzel des Dateisystems anzulegen touch /test.txt || echo darf nicht die Dateisystemwurzel schreiben
Status eines Befehls auswerten
Manche Befehle kennen nicht nur Erfolg oder Misserfolg, sondern liefern einen genaueren Status. Direkt nach der Ausführung lässt sich dieser aus der speziellen Variable $? auslesen. Der Erfolg hat üblicherweise den Status 0 und der „normale Misserfolg“ 1. Hier ein Beispiel wo ls den Status 2 zurückliefert.
# versuche eine nicht vorhandene Datei aufzulisten ls aslkdjaslkdj13123 # prüfe Status echo $?
Große Mengen an Dateien verarbeiten
Nutzt man ein Wildcard-Zeichen wie den Asterisk („Stern“) * um viele Dateien gleichzeitig zu verarbeiten, so kann das bei sehr vielen Dateien an eine Grenze stoßen, wenn die Bash nicht genug Speicherplatz hat, um diese lange Liste zu erzeugen. Die sogenannte Expansion durch die Bash schlägt fehl und der Befehl wird gar nicht erst ausgeführt.
ls -l grafix/*.jpg
Für die weitere Erklärung nehmen wir an, dass das Verzeichnis grafix einige 10000 Dateien mit der Endung jpg enthält. Nebenbei werden mit nachfolgenden Befehlen auch die Dateien in Unterverzeichnissen gefunden.
Bei manchen Befehlen kann man den Stern in einfache Anführungszeichen setzen und er macht dann die Expansion intern selbst. Aber das beherrschen nicht alle. Hier kann es helfen, die Befehle zu kombinieren. Der Befehl find beherrscht die interne Expansion. So bekommen wir eine Liste der gesuchten Dateien, aber er kann selbst nicht viel damit machen. Aber er kann andere Befehle ausführen:
find grafix -name '*.jpg' -exec ls -l '{}' \;
Das funktioniert schon mal relativ gut, ist nun aber recht langsam, weil ls für jede Datei einzeln aufgerufen wird. Hier kommt ein weiterer nützlicher Befehl ins Spiel. Der Befehl xargs kann lange Listen in kurze Listen umwandeln. Und er kann die Bearbeitung der Listen auch noch mit der Option -P Parallelisieren und auf mehreren Prozessoren gleichzeitig laufen lassen. Hier die vollständige Version, die nur reguläre Dateien bearbeitet und dabei auch Leerzeichen im Dateinamen verträgt:
find grafix -type f -name '*.jpg' -print0 | xargs -0 -P 4 ls -l
Wichtig ist hier noch zu verstehen, dass find nicht einfach Optionen hat. Es wird vielmehr eine Art kleines Programm durchlaufen. Wenn dieses Mini-Programm keine Print-Anweisung enthält, wird am Ende ein einfaches print ausgeführt. Steht das print jedoch irgendwo weiter vorn explizit, werden die zu diesem Punkt im Programm gefundenen Dateien ausgegeben und die nachfolgenden Regeln ignoriert. Anders gesagt: In den meisten Fällen gehört das print-Statement ans Ende der Liste der Parameter.
Nun haben wir also auf geschickte Weise die drei Befehle find, xargs und ls kombiniert, um das gewünschte Ziel zu erreichen.