Ich hatte viel Spaß beim Schreiben dieses Buchs und hoffe, Sie hatten beim Lesen ebenso viel Spaß. Im letzten Akt wollen wir einige kleinere Themen behandeln, die nicht so ganz in die früheren Kapitel passten. Diese Themen haben mich zu einem besseren Linux-Anwender gemacht, und vielleicht helfen sie Ihnen ja auch.
Die folgenden Zeitsparer lassen sich leicht in wenigen Minuten erlernen.
Wenn Sie eine Textdatei mit less anschauen und die Datei bearbeiten wollen, beenden Sie less nicht. Drücken Sie einfach v, um Ihren bevorzugten Texteditor aufzurufen. Dieser lädt die Datei und setzt den Cursor genau an die Stelle, an der Sie in less gerade waren. Wenn Sie den Editor beenden, landen Sie wieder in less an der ursprünglichen Stelle.
Damit dieser Trick am besten funktioniert, setzen Sie die Umgebungsvariable EDITOR und/oder VISUAL auf einen Bearbeitungsbefehl. Diese Umgebungsvariablen repräsentieren Ihren Standard-Linux-Texteditor, der durch verschiedene Programme gestartet wird, darunter less, lynx, git, crontab, und zahlreiche E-Mail-Programme. Um zum Beispiel emacs als Ihren Standardeditor festzulegen, platzieren Sie eine (oder beide) der folgenden Zeilen in eine Shell-Konfigurationsdatei und laden sie:
VISUAL=emacs
EDITOR=emacs
Wenn Sie diese Variablen nicht setzen, wird jeweils der Editor gestartet, den das Linux-System vorgibt. Üblicherweise ist das vim. Keine Panik, falls Sie irgendwie in vim landen und keine Ahnung haben, wie Sie ihn benutzen sollen. Sie beenden vim, indem Sie die Esc-Taste drücken und :q! (einen Doppelpunkt, den Buchstaben q und ein Ausrufezeichen) eintippen und dann Enter drücken. Um den emacs zu beenden, drücken Sie Strg-X, gefolgt von Strg-C.
Sie wollen jede Datei im aktuellen Verzeichnis bearbeiten, die einen bestimmten String (oder regulären Ausdruck) enthält? Generieren Sie eine Liste von Dateinamen mit grep -l und übergeben Sie diese mittels Befehlssubstitution an Ihren Editor. Angenommen, Ihr Editor ist vim, dann lautet der Befehl folgendermaßen:
$ vim $(grep -l string *)
Bearbeiten Sie alle Dateien in einem kompletten Verzeichnisbaum (aktuelles Verzeichnis und alle Unterverzeichnisse), die String enthalten, indem Sie die Option -r (rekursiv) zu grep hinzufügen und im aktuellen Verzeichnis (dem Punkt) beginnen:
$ vim $(grep -lr String .)
Für ein schnelleres Durchsuchen großer Verzeichnisbäume benutzen Sie find mit xargs anstelle von grep -r:
$ vim $(find . -type f -print0 | xargs -0 grep -l String)
»Technik #3: Befehlssubstitution« auf Seite 132 behandelte diese Technik bereits, aber ich wollte sie noch einmal hervorheben, weil sie so nützlich ist. Denken Sie daran, auf Dateinamen zu achten, die Leerzeichen und Zeichen mit besonderer Bedeutung für die Shell enthalten, da diese Ihre Ergebnisse verfälschen können, wie ich in »Sonderzeichen und Befehlssubstitution« auf Seite 133 erläutert habe.
Falls Sie einen Befehl konsistent falsch schreiben, sollten Sie einen Alias für Ihre häufigsten Fehler definieren, damit dennoch die richtigen Befehle ausgeführt werden:
alias firfox=firefox
alias les=less
alias meacs=emacs
Achten Sie darauf, einen bestehenden Linux-Befehl nicht versehentlich außer Kraft zu setzen oder zu übergehen (Shadowing), indem Sie einen Alias mit demselben Namen definieren. Suchen Sie zuerst mit dem Befehl which oder type (siehe »Auszuführende Programme auffinden« auf Seite 47) nach dem vorgesehenen Alias und führen Sie den Befehl man aus, um sicherzugehen, dass es keinen anderen Befehl desselben Namens gibt:
$ type firfox
bash: type: firfox: not found
$ man firfox
No manual entry for firfox
Es gibt mehrere Möglichkeiten, in Linux leere Dateien anzulegen. Der Befehl touch, der den Zeitstempel einer Datei aktualisiert, erzeugt auch eine Datei, falls sie noch nicht existiert:
$ touch newfile1
touch eignet sich großartig, um viele leere Dateien für Tests zu erzeugen:
$ mkdir tmp Erzeugt ein Verzeichnis.
$ cd tmp
$ touch file{0000..9999}.txt Erzeugt 10.000 Dateien.
$ cd ..
$ rm -rf tmp Entfernt das Verzeichnis und die Dateien.
Der echo-Befehl stellt eine leere Datei her, wenn Sie seine Ausgabe in eine Datei umleiten, allerdings müssen Sie ihn mit der Option -n ausführen:
$ echo -n > newfile2
Wenn Sie die Option -n vergessen, enthält die resultierende Datei ein Zeichen, nämlich ein Newline, und ist damit nicht leer.
Wenn Sie eine Datei Zeile für Zeile verarbeiten müssen, schicken Sie sie mit cat in eine while read-Schleife:
$ cat myfile | while read line; do
...hier wird etwas gemacht...
done
Um zum Beispiel die Länge der einzelnen Zeilen einer Datei zu berechnen, etwa von /etc/hosts, schicken Sie die einzelnen Zeilen mit einer Pipeline an wc -c:
$ cat /etc/hosts | while read line; do
echo "$line" | wc -c
done
65
31
1
...
Ein praktischeres Beispiel für diese Technik finden Sie in Beispiel 9-3.
In »Der find-Befehl« auf Seite 91 habe ich find -exec vorgestellt, das einen beliebigen Linux-Befehl rekursiv auf einen ganzen Verzeichnisbaum anwendet:
$ find . -exec Ihr Befehl steht hier \;
Bestimmte andere Befehle unterstützen von sich aus eine Rekursion. Wenn Ihnen das bewusst ist, können Sie sich Arbeit sparen, indem Sie deren eigene Rekursion benutzen, anstatt einen find-Befehl zu konstruieren:
ls -R
Zum rekursiven Auflisten von Verzeichnissen und deren Inhalten.
cp -r oder cp -a
Zum rekursiven Kopieren von Verzeichnissen und deren Inhalten.
rm -r
Zum rekursiven Löschen von Verzeichnissen und deren Inhalten.
grep -r
Zum Durchsuchen eines Verzeichnisbaums anhand eines regulären Ausdrucks.
chmod -R
Zum rekursiven Ändern der Dateimodi (Schutzmaßnahmen).
chown -R
Zum rekursiven Ändern der Dateieigentümerschaft.
chgrp -R
Zum rekursiven Ändern der Gruppenzugehörigkeit einer Datei.
Suchen Sie sich einen gebräuchlichen Befehl aus, wie etwa cut oder grep, und lesen Sie dessen Manpage gründlich durch. Sie werden vermutlich die eine oder andere Option entdecken, die Sie noch nie benutzt haben, die Ihnen aber dennoch nützlich sein kann. Wiederholen Sie dies immer wieder einmal und erweitern Sie auf diese Weise Ihren Linux-Werkzeugkasten.
Die folgenden Techniken erfordern einen wirklichen Lernaufwand, der sich aber in gesparter Zeit auszahlt. Ich liefere hier nur einen Vorgeschmack auf die einzelnen Techniken, weil ich Ihnen nicht alle Einzelheiten vorstellen kann, sondern Sie anregen möchte, sich selbst auf die Entdeckungsreise zu begeben.
Führen Sie man bash aus, lassen Sie sich die offizielle Dokumentation zu bash anzeigen und lesen Sie das ganze Ding – ja, alle 46.318 Wörter darin:
$ man bash | wc -w
46318
Nehmen Sie sich ein paar Tage Zeit. Arbeiten Sie sie langsam durch. Sie werden auf jeden Fall eine Menge dadurch lernen, was Ihren täglichen Linux-Einsatz erleichtern wird.
In »Ein erstes Beispiel: Dateien finden« auf Seite 177 gibt es eine kurze Anmerkung zum Planen von Befehlen, um sie künftig in regelmäßigen Abständen automatisch auszuführen. Ich empfehle Ihnen, das Programm crontab kennenzulernen, mit dem Sie selbst Befehle eintakten können. Sie könnten zum Beispiel einen Zeitplan aufstellen, um Dateien regelmäßig auf ein externes Laufwerk zu sichern oder sich selbst Erinnerungen an eine monatliche Veranstaltung per E-Mail zu senden.
Bevor Sie crontab ausführen, definieren Sie Ihren Standardeditor, wie in »Aus less in Ihren Editor springen« auf Seite 211 gezeigt. Führen Sie dann crontab -e aus, um Ihre persönliche Datei mit den zeitlich geplanten Befehlen zu bearbeiten. crontab startet Ihren Standardeditor und öffnet eine leere Datei, um die Befehle festzulegen. Diese Datei wird als Ihre Crontab bezeichnet.
Ein Befehl in einer Crontab-Datei, oft auch Cronjob genannt, besteht aus sechs Feldern, die alle auf einer (möglicherweise sehr langen) Zeile stehen. Die ersten fünf Felder geben den Zeitplan des Jobs in Minute, Stunde, Tag des Monats, Monat bzw. Wochentag an. Das sechste Feld ist der auszuführende Linux-Befehl. Sie können einen Befehl stündlich, täglich, wöchentlich, monatlich, jährlich, zu bestimmten Tagen oder Zeiten oder auch in komplexeren Arrangements starten. Einige Beispiele:
* * * * * Befehl Führt den Befehl einmal in der Minute aus.
30 7 * * * Befehl Führt den Befehl jeden Tag um 07:30 Uhr aus.
30 7 5 * * Befehl Führt den Befehl am 5. Tag jedes Monats um 07:30 Uhr aus.
30 7 5 1 * Befehl Führt den Befehl immer am 5. Januar um 07:30 Uhr aus.
30 7 * * 1 Befehl Führt den Befehl jeden Montag um 07:30 Uhr aus.
Nachdem Sie alle sechs Felder eingerichtet, die Datei beendet und Ihren Editor verlassen haben, wird der Befehl automatisch (von einem Programm namens cron) entsprechend dem von Ihnen definierten Zeitplan aufgerufen. Die Syntax für die Zeitpläne ist kurz und kryptisch, aber in der Manpage (man 5 crontab) sowie in zahlreichen Online-tutorials gut dokumentiert (suchen Sie nach cron tutorial).
Ich empfehle Ihnen außerdem, den Befehl at zu erlernen, der Befehle für die einmalige Ausführung zu einem bestimmten Datum und einer bestimmten Uhrzeit einrichtet. Näheres erfahren Sie, wenn Sie man at aufrufen. Hier ist ein Befehl, der Ihnen morgen um 10:00 Uhr eine E-Mail sendet, um Sie daran zu erinnern, sich die Zähne zu putzen:
$ at 22:00 tomorrow
Warnung: commands will be executed using /bin/sh
at> echo Putz dir die Zähne | mail $USER
at> ^D Tippen Sie Strg-D, um die Eingabe zu beenden.
job 699 at Sun Nov 14 22:00:00 2021
Um Ihre anstehenden at-Jobs aufzulisten, führen Sie atq aus:
$ atq
699 Sun Nov 14 22:00:00 20211 a smith
Und um die Befehle in einem at-Job zu sehen, führen Sie at -c mit der Jobnummer aus und lassen sich die letzten paar Zeilen anzeigen:
$ at -c 699 | tail
...
echo Putz dir die Zähne | mail $USER
Wenn Sie einen anstehenden Job entfernen wollen, bevor er ausgeführt wird, rufen Sie atrm mit der Jobnummer auf:
$ atrm 699
Um ein vollständiges Verzeichnis einschließlich seiner Unterverzeichnisse von einer Festplatte auf eine andere zu kopieren, verwenden viele Linux-Benutzer den Befehl cp -r oder cp -a:
$ cp -a dir1 dir2
cp macht das beim ersten Mal ganz gut, aber wenn Sie anschließend einige Dateien im Verzeichnis dir1 ändern und das Kopieren erneut ausführen, ist cp eine Verschwendung. Es kopiert ganz pflichtbewusst alle Dateien und Verzeichnisse wieder von dir1, obwohl in dir2 bereits identische Kopien existieren.
Der Befehl rsync ist ein clevereres Kopierprogramm. Er kopiert nur die Unterschiede zwischen dem ersten und dem zweiten Verzeichnis.
$ rsync -a dir1/ dir2
![]() |
Hinweis Der Schrägstrich in dem gezeigten Befehl bedeutet, dass die Dateien innerhalb von dir1 kopiert werden sollen. Ohne den Schrägstrich würde rsync das Verzeichnis dir1 selbst kopieren, wodurch dir2/dir1 entsteht. |
Falls Sie später eine Datei dem Verzeichnis dir1 hinzufügen, kopiert rsync nur diese eine Datei. Ändern Sie eine Zeile in einer Datei in dir1, kopiert rsync diese eine Zeile! Das ist eine ungeheure Zeitersparnis, wenn große Verzeichnisbäume mehrmals kopiert werden. rsync kann sogar über eine SSH-Verbindung auf einen entfernten Server kopieren.
rsync besitzt Dutzende von Optionen. Diese hier sind ganz besonders nützlich:
-v (»verbose«, also wortreich)
Zum Ausgeben der Namen der Dateien, während sie kopiert werden.
Um vorzugeben, dass kopiert wird; kombinieren Sie diese Option mit -v, um festzustellen, welche Dateien kopiert werden würden.
-x
Um rsync mitzuteilen, dass es die Grenzen des Dateisystems nicht überschreiten soll.
Ich empfehle Ihnen dringend, sich mit rsync vertraut zu machen, um effizienter zu kopieren. Lesen Sie die Manpage und schauen Sie sich Beispiele in dem Artikel »Rsync Examples in Linux« (https://oreil.ly/7gHCi) von Korbin Brown an.
Shell-Skripte sind bequem und leistungsstark, haben aber einige ernsthafte Schwächen. Zum Beispiel sind sie schrecklich im Umgang mit Dateinamen, die Whitespace-Zeichen enthalten. Betrachten Sie dieses kurze bash-Skript, das versucht, eine Datei zu entfernen:
#!/bin/bash
BOOKTITLE="Slow Inefficient Linux"
rm $BOOKTITLE # Falsch! Machen Sie das nicht!
Es sieht aus, als würde die zweite Zeile eine Datei namens Slow Inefficient Linux entfernen, aber das tut sie nicht. Sie versucht stattdessen, drei Dateien namens Slow, Inefficient und Linux zu löschen. Die Shell erweitert die Variable $BOOKTITLE, bevor sie rm aufruft, und ihre Erweiterung ergibt drei Wörter, die durch Whitespace getrennt sind – so als hätten Sie Folgendes eingegeben:
rm Slow Efficient Linux
Die Shell löst dann rm mit drei Argumenten aus, und es kommt potenziell zu einer Katastrophe, wenn sie die falschen Dateien entfernt. Ein korrekter Löschbefehl würde $BOOKTITLE mit doppelten Anführungszeichen umgeben:
rm "$BOOKTITLE"
was von der Shell so erweitert wird:
rm "Slow Efficient Linux"
Diese Art von subtiler, potenziell destruktiver Eigenart ist nur ein Beispiel dafür, wie ungeeignet das Shell-Skripting für ernsthafte Projekte ist. Ich empfehle Ihnen daher, eine zweite Skriptsprache zu lernen, wie etwa Perl, PHP, Python oder Ruby. Alle verarbeiten Whitespace korrekt. Alle unterstützen echte Datenstrukturen. Alle besitzen leistungsstarke Funktionen zur String-Verarbeitung. Alle können leicht Berechnungen durchführen. Die Liste der Vorteile ist schier endlos.
Verwenden Sie die Shell, um komplexe Befehle zu starten und einfache Skripte anzulegen. Für aufwendigere Aufgaben sollten Sie sich einer anderen Sprache zuwenden. Suchen Sie sich online ein passendes Tutorial und legen Sie los!
Das Programm make aktualisiert Dateien automatisch anhand von Regeln. Es ist dafür gedacht, die Softwareentwicklung zu beschleunigen, aber mit ein bisschen Aufwand kann make auch andere Aspekte Ihres Linux-Lebens vereinfachen.
Nehmen wir einmal an, Sie hätten drei Dateien namens chapter1.txt, chapter2.txt und chapter3.txt, an denen Sie getrennt arbeiten. Außerdem haben Sie eine vierte Datei, nämlich book.txt, die eine Kombination der drei Kapiteldateien darstellt. Immer wenn sich ein Kapitel ändert, müssen Sie book.txt neu kombinieren und aktualisieren, zum Beispiel mit einem solchen Befehl:
$ cat chapter1.txt chapter2.txt chapter3.txt > book.txt
Diese Situation ist perfekt für den Einsatz von make. Sie haben:
make liest eine Konfigurationsdatei, üblicherweise Makefile genannt, die voller Regeln und Befehle ist. So besagt zum Beispiel die folgende Makefile-Regel, dass book.txt von den drei Kapiteldateien abhängig ist:
book.txt: chapter1.txt chapter2.txt chapter3.txt
Wenn das Ziel, oder auch Target, der Regel (in diesem Fall book.txt) älter ist als eine seiner Abhängigkeiten (die Kapiteldateien), betrachtet make das Ziel als veraltet. Falls Sie auf der Zeile nach der Regel einen Befehl angeben, führt make den Befehl aus, um das Ziel zu aktualisieren:
book.txt: chapter1.txt chapter2.txt chapter3.txt
cat chapter1.txt chapter2.txt chapter3.txt > book.txt
Um die Regel anzuwenden, führen Sie einfach den Befehl make aus:
$ ls
Makefile chapter1.txt chapter2.txt chapter3.txt
$ make
cat chapter1.txt chapter2.txt chapter3.txt > book.txt Ausgeführt von make
$ ls
Makefile book.txt chapter1.txt chapter2.txt chapter3.txt
$ make
make: 'book.txt' is up to date.
$ vim chapter2.txt Aktualisieren eines Kapitels
$ make
cat chapter1.txt chapter2.txt chapter3.txt > book.txt
make wurde für Programmierer entwickelt, doch mit ein bisschen Aufwand können Sie es auch für Nichtprogrammieraufgaben verwenden. Immer wenn Sie Dateien aktualisieren müssen, die von anderen Dateien abhängen, können Sie Ihre Arbeit vermutlich vereinfachen, indem Sie sich ein Makefile schreiben.
Mir half make dabei, dieses Buch zu schreiben und zu debuggen. Ich habe das Buch in einer Auszeichnungssprache namens AsciiDoc geschrieben und die Kapitel regelmäßig nach HTML konvertiert, um sie mir in einem Browser anzuschauen. Hier ist eine make-Regel zum Konvertieren einer AsciiDoc-Datei in eine HTML-Datei:
%.html: %.asciidoc
asciidoctor -o $@ $<
Es bedeutet: Um eine Datei mit der Erweiterung .html (%.html) zu erzeugen, wird nach einer entsprechenden Datei mit der Erweiterung .asciidoc (%.asciidoc) gesucht. Falls die HTML-Datei älter ist als die AsciiDoc-Datei, wird die HTML-Datei regeneriert, indem der Befehl asciidoctor auf der abhängigen Datei ($<) ausgeführt wird, wobei die Ausgabe an die Ziel-HTML-Datei geschickt wird (-o $@). Mit dieser etwas kryptischen, aber kurzen Regel an Ort und Stelle tippe ich einfach nur den Befehl make ein, um die HTML-Version des Kapitels herzustellen, das Sie gerade lesen. make startet asciidoctor, um die Aktualisierung durchzuführen:
$ ls ch11*
ch11.asciidoc
$ make ch11.html
asciidoctor -o ch11.html ch11.asciidoc
$ ls ch11*
ch11.asciidoc ch11.html
$ firefox ch11.html Betrachten der HTML-Datei
Es dauert kaum eine Stunde, sich mit make so weit anzufreunden, dass es für kleine Aufgaben genutzt werden kann. Der Aufwand lohnt sich. Eine hilfreiche Anleitung befindet sich unter makefiletutorial.com (https://makefiletutorial.com/).
Wollten Sie schon einmal eine Datei bearbeiten, hatten aber Angst, dass Ihre Änderungen alles kaputtmachen? Vielleicht haben Sie eine Sicherungskopie angelegt und dann das Original bearbeitet, sodass Sie wussten, dass Sie das Backup wiederherstellen können, falls Sie einen Fehler machen:
$ cp myfile myfile.bak
Diese Lösung ist allerdings nicht skalierbar. Was, wenn Sie Dutzende oder Hunderte von Dateien haben oder Hunderte von Menschen, die an ihnen arbeiten? Versionskontrollsysteme wie Git und Subversion wurden erfunden, um dieses Problem ganz allgemein zu lösen, indem mehrere Versionen einer Datei in geeigneter Weise im Auge behalten werden.
Git ist für die Wartung von Softwarequellcode sehr verbreitet. Ich empfehle jedoch, sich damit auch zu beschäftigen, wenn Sie wichtige Textdateien verwalten möchten, deren Änderungen wesentlich sind. Vielleicht sind es persönliche Dateien oder Betriebssystemdateien in /etc. »Mit Ihrer Umgebung verreisen« auf Seite 128 schlägt vor, die Konfigurationsdateien Ihrer bash mithilfe einer Versionskontrolle zu verwalten.
Ich habe Git eingesetzt, als ich dieses Buch schrieb, um verschiedene Methoden auszuprobieren, das Material zu präsentieren. Ohne allzu viel Aufwand habe ich drei Versionen des Buchs hergestellt und gepflegt – eine für das komplette Manuskript, eine, die nur die Dateien enthielt, die ich meinem Verleger zur Begutachtung übermittelt habe, und eine für experimentelle Arbeiten, bei denen ich neue Ideen ausprobiert habe. Wenn mir nicht gefiel, was ich geschrieben habe, konnte ich mit nur einem Befehl eine frühere Version wiederherstellen.
Es kann nicht Ziel dieses Buchs sein, Git zu lehren. Aber hier stelle ich Ihnen einige Beispielbefehle vor, anhand deren Sie den grundsätzlichen Arbeitsablauf sehen können und die Ihnen Lust auf mehr machen sollen. Verwandeln Sie Ihr aktuelles Verzeichnis (und alle seine Unterverzeichnisse) in ein Git-Repository:
$ git init
Bearbeiten Sie einige Befehle. Anschließend fügen Sie die geänderten Dateien einer unsichtbaren »Staging Area« hinzu, eine Operation, die Ihre Absicht ausdrückt, eine neue Version zu erzeugen:
$ git add .
Erzeugen Sie die neue Version, wobei Sie in einem Kommentar die Änderungen an den Dateien beschreiben:
$ git commit -m"Geändert X nach Y"
Betrachten Sie Ihre Versions-History:
$ git log
Es gibt natürlich noch viel mehr, wie etwa das Zurückholen alter Versionen von Dateien und das Speichern (Pushing) von Versionen auf einem anderen Server. Suchen Sie sich ein git-Tutorial (https://oreil.ly/0AlOu) und fangen Sie an!
Ich danke Ihnen ganz herzlich, dass Sie mir durch dieses Buch gefolgt sind. Ich hoffe, es hat das Versprechen gehalten, das ich in der Einführung gemacht habe – das Niveau Ihrer Linux-Kommandozeilenkenntnisse zu verbessern. Berichten Sie mir von Ihren Erfahrungen; Sie können mir an dbarrett@oreilly.com schreiben. Ich wünsche Ihnen weiterhin viel Spaß mit Linux!