Einfacher (»Plain«) Text ist auf vielen Linux-Systemen das gebräuchlichste Datenformat. Der Inhalt, der in den meisten Pipelines von Befehl zu Befehl geschickt wird, ist Text. Die Quellcodedateien von Programmierern, die Systemkonfigurationsdateien in /etc sowie HTML- und Markdown-Dateien sind Textdateien. E-Mail-Nachrichten sind Text; selbst Anhänge werden intern zum Transport als Text gespeichert. Sie könnten sogar alltägliche Dateien wie Einkaufslisten und persönliche Notizen als Text speichern.
Betrachten Sie demgegenüber einmal das heutige Internet, einen Mischmasch aus Streaming-Audio und -Video, Social-Media-Posts, im Browser vorliegenden Dokumenten in Google Docs und Office 365, PDFs und anderen Rich-Media-Daten. (Ganz zu schweigen von den Daten, die von mobilen Apps verarbeitet werden, die das Konzept einer »Datei« vor einer ganzen Generation versteckt haben.) Vor diesem Hintergrund wirken einfache Textdateien geradezu altmodisch.
Dennoch kann jede Textdatei zu einer reichhaltigen Datenquelle werden, die Sie mit sorgfältig gestalteten Linux-Befehlen ausbeuten können, vor allem wenn der Text strukturiert ist. Jede Zeile in der Datei /etc/passwd zum Beispiel repräsentiert einen Linux-Benutzer und besitzt sieben Felder, einschließlich des Benutzernamens, einer numerischen Benutzer-ID, des Home-Verzeichnisses und mehr. Die Felder sind durch Doppelpunkte getrennt, sodass die Datei leicht mithilfe von cut -d: oder awk -F: analysiert werden kann. Hier ist ein Befehl, der alle Benutzernamen (das erste Feld) alphabetisch ausgibt:
$ cut -d: -f1 /etc/passwd | sort
avahi
backup
daemon
...
Und hier ist einer, der menschliche Benutzer anhand ihrer numerischen Benutzer-IDs von Systemkonten trennt und den Benutzern eine Willkommens-E-Mail schickt. Bauen wir uns diesen Einzeiler Schritt für Schritt zusammen. Zuerst verwenden Sie awk, um die Benutzernamen (Feld 1) auszugeben, wenn die numerische Benutzer-ID (Feld 3) 1000 oder größer ist:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd
jones
smith
Dann werden die Grüße erzeugt, und zwar mittels einer Pipeline an xargs:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo "Hallo, @!"
Hallo, jones!
Hallo, smith!
Danach generieren Sie Befehle (Strings), um die einzelnen Begrüßungen in einer Pipeline an den Befehl mail zu leiten, der eine E-Mail mit einer bestimmten Betreffzeile (-s) an einen bestimmten Benutzer sendet:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hi there, @!" | mail -s Gruesse @'
echo "Hallo, jones!" | mail -s Gruesse jones
echo "Hallo, smith!" | mail -s Gruesse smith
Schließlich leiten Sie die generierten Befehle mit einer Pipeline an die bash, um die E-Mails zu senden:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hallo, @!" | mail -s Gruesse @' \
| bash
echo "Hallo, jones!" | mail -s Gruesse jones
echo "Hallo, smith!" | mail -s Gruesse smith
Die gezeigten Lösungen beginnen, wie so viele andere in diesem Buch, mit einer vorhandenen Textdatei und manipulieren deren Inhalt mit Befehlen. Es wird Zeit, diesen Ansatz umzukehren und bewusst neue Textdateien zu entwerfen, die gut mit Linux-Befehlen zusammenspielen.1 Es ist eine Gewinnerstrategie, um wirklich effizient auf einem Linux-System zu arbeiten. Alles, was nötig ist, sind vier Schritte:
In diesem Kapitel konstruieren Sie eine Vielzahl an strukturierten Textdateien und erzeugen Befehle, um diese zu verarbeiten und damit verschiedene Probleme zu lösen.
Nehmen wir einmal an, Ihr Home-Verzeichnis enthielte Zehntausende von Dateien und Unterverzeichnissen und Sie könnten sich mal wieder nicht daran erinnern, wohin Sie etwas gespeichert haben. Der Befehl find findet eine Datei anhand des Namens, also etwa animals.txt:
$ find $HOME -name animals.txt -print
/home/smith/Work/Writing/Books/Lists/animals.txt
Allerdings ist find langsam, weil es Ihr gesamtes Home-Verzeichnis durchsucht und Sie regelmäßig Dateien finden müssen. Dies ist Schritt 1, Erkennen eines Geschäftsproblems, das mit Daten zu tun hat: schnelles Auffinden von Dateien anhand ihres Namens in Ihrem Home-Verzeichnis.
Schritt 2 besteht darin, die Daten in einem passenden Format in einer Textdatei zu speichern. Führen Sie einmal find aus, um eine Liste all Ihrer Dateien und Verzeichnisse mit einem Dateipfad pro Zeile zu erstellen. Speichern Sie diese Liste in einer verborgenen Datei:
$ find $HOME -print > $HOME/.ALLFILES
$ head -n3 $HOME/.ALLFILES
/home/smith
/home/smith/Work
/home/smith/Work/resume.pdf
...
Nun haben Sie die Daten: einen zeilenweisen Index Ihrer Dateien. In Schritt 3 geht es darum, Linux-Befehle zu erfinden, mit denen sich die Suche nach Dateien beschleunigen lässt. Dazu verwenden wir grep. Es ist viel schneller, mit grep durch eine große Datei zu gehen, als find in einem großen Verzeichnisbaum auszuführen:
$ grep animals.txt $HOME/.ALLFILES
/home/smith/Work/Writing/Books/Lists/animals.txt
Schritt 4 besteht darin, die Ausführung des Befehls zu erleichtern. Schreiben Sie ein einzeiliges Skript namens ff für find files, das grep mit vom Benutzer vorgegebenen Optionen und einem Suchstring ausführt, wie in Beispiel 9-1 gezeigt.
#!/bin/bash
# $@ bedeutet alle Argumente, die dem Skript übergeben werden.
grep "$@" $HOME/.ALLFILES
Machen Sie das Skript ausführbar und legen Sie es in einem Verzeichnis in Ihrem Suchpfad ab, also etwa in Ihrem persönlichen bin-Unterverzeichnis:
$ chmod+xff
$ echo $PATH Überprüfen Sie Ihren Suchpfad.
/home/smith/bin:/usr/local/bin:/usr/bin:/bin
$ mv ff ~/bin
Führen Sie immer dann ff aus, wenn Sie schnell Dateien finden wollen, sich aber nicht erinnern können, wo Sie sie abgelegt haben.
$ ff animal
/home/smith/Work/Writing/Books/Lists/animals.txt
$ ff -i animal | less Groß-/Kleinschreibung spielt bei diesem grep keine Rolle.
/home/smith/Work/Writing/Books/Lists/animals.txt
/home/smith/Vacations/Zoos/Animals/pandas.txt
/home/smith/Vacations/Zoos/Animals/tigers.txt
...
$ ff -i animal | wc -l Wie viele Treffer?
16
Führen Sie immer wieder mal den Befehl find aus, um den Index zu aktualisieren. (Oder besser noch: Erzeugen Sie mit cron einen Job, der regelmäßig ausgeführt wird; siehe »Lernen Sie cron, crontab und at« auf Seite 215.) Voilà – Sie haben sich aus zwei kleinen Befehlen ein schnelles, flexibles Dateisuchwerkzeug gebaut. Linux-Systeme bieten auch andere Anwendungen, die schnell Dateien indizieren und suchen, wie etwa den Befehl locate und die Suchhilfsprogramme in GNOME, KDE Plasma und anderen Desktopumgebungen, aber das ist hier egal. Schauen Sie einfach, wie leicht es war, selbst etwas zu bauen. Und der Schlüssel zum Erfolg bestand in einer Textdatei in einem einfachen Format.
Für das nächste Beispiel wollen wir einmal annehmen, dass Sie einige Internet-Domainnamen besitzen und im Auge behalten wollen, wann diese ablaufen, damit Sie sie rechtzeitig erneuern können. Das ist Schritt 1, Identifizieren des Geschäftsproblems. Schritt 2 ist es, eine Datei mit diesen Domainnamen zu erzeugen, also etwa domains.txt, mit einem Domainnamen pro Zeile:
example.com
oreilly.com
efficientlinux.com
...
In Schritt 3 sollen Befehle erfunden werden, die diese Textdatei nutzen, um die Ablaufdaten zu ermitteln. Beginnen Sie mit dem Befehl whois, der eine Registrierungsstelle nach Informationen über eine Domain abfragt:
$ whois example.com | less
Domain Name: EXAMPLE.COM
Registry Domain ID: 2336799_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.iana.org
Updated Date: 2021-08-14T07:01:44Z
Creation Date: 1995-08-14T04:00:00Z
Registry Expiry Date: 2022-08-13T04:00:00Z
...
Dem Ablaufdatum ist der String Registry Expiry Date vorangestellt, den Sie mit grep und awk isolieren können:
$ whois example.com | grep 'Registry Expiry Date:'
Registry Expiry Date: 2022-08-13T04:00:00Z
$ whois example.com | grep 'Registry Expiry Date:' | awk '{print $4}'
2022-08-13T04:00:00Z
Verbessern Sie die Lesbarkeit des Datums mit dem Befehl date --date, der einen Datumsstring aus einem Format in ein anderes konvertieren kann:
$ date --date 2022-08-13T04:00:00Z
Sat Aug 13 00:00:00 EDT 2022
$ date --date 2022-08-13T04:00:00Z +'%Y-%m-%d' Jahr-Monat-Tag-Format
2022-08-13
Benutzen Sie eine Befehlssubstitution, um den Datumsstring aus whois an den date-Befehl zu übergeben:
$ echo $(whois example.com | grep 'Registry Expiry Date:' | awk '{print $4}')
2022-08-13T04:00:00Z
$ date \
--date $(whois example.com \
| grep 'Registry Expiry Date:' \
| awk '{print $4}') \
+'%Y-%m-%d'
2022-08-13
Sie haben nun einen Befehl, der eine Registrierungsstelle abfragt und ein Ablaufdatum ausgibt. Stellen Sie ein Skript check-expiry her, gezeigt in Beispiel 9-2, das den vorhergehenden Befehl ausführt und das Ablaufdatum, ein Tab und den Domainnamen ausgibt:
$ ./check-expiry example.com
2022-08-13 example.com
Beispiel 9-2: Das Skript check-expiry
#!/bin/bash
expdate=$(date \
--date $(whois "$1" \
| grep 'Registry Expiry Date:' \
| awk '{print $4}') \
+'%Y-%m-%d')
echo "$expdate $1" # Zwei Werte, getrennt durch ein Tab.
Prüfen Sie mithilfe einer Schleife alle Domains in der Datei domains.txt. Erzeugen Sie ein neues Skript, check-expiry-all, wie in Beispiel 9-3 zu sehen.
Beispiel 9-3: Das Skript check-expiry-all
#!/bin/bash
cat domains.txt | while read domain; do
./check-expiry "$domain"
sleep 5 # Seien Sie freundlich zum Server der Registrierungsstelle.
done
Führen Sie das Skript im Hintergrund aus, da es eine Weile dauern kann, sofern Sie viele Domains haben, und leiten Sie alle Ausgaben (Standardausgabe und Standardfehlerausgabe) in eine Datei um:
$ ./check-expiry-all &> expiry.txt &
Wenn das Skript fertig ist, enthält die Datei expiry.txt die gewünschten Informationen:
$ cat expiry.txt
2022-08-13 example.com
2022-05-26 oreilly.com
2022-09-17 efficientlinux.com
...
Hurra! Aber hören Sie hier nicht auf. Die Datei expiry.txt ist selbst ganz gut strukturiert und eignet sich mit ihren zwei Spalten, die durch Tabs getrennt sind, für eine Weiterverarbeitung. Sortieren Sie zum Beispiel die Datumsangaben und suchen Sie die Domain, die als Nächstes erneuert werden muss:
$ sort -n expiry.txt | head -n1
2022-05-26 oreilly.com
Oder nutzen Sie awk, um Domains zu finden, die abgelaufen sind oder am heutigen Tag ablaufen – das heißt, ihr Ablaufdatum (Feld 1) ist kleiner oder gleich dem heutigen Datum (ausgegeben mit date +%Y-%m-%d):
$ awk "\$1<=\"$(date +%Y-%m-%d)\"" expiry.txt
Einige Hinweise zu dem gerade gezeigten awk-Befehl:
Mit etwas mehr Aufwand könnten Sie Datumsberechnungen in awk durchführen, um Ablaufdaten zum Beispiel zwei Wochen im Voraus zu melden und dann einen regelmäßig ausgeführten Job einzurichten, der das Skript jede Nacht ausführt und Ihnen per E-Mail einen Bericht zuschickt. Experimentieren Sie ruhig ein wenig. Entscheidend ist hier wieder, dass Sie mit nur einer Handvoll von Befehlen ein nützliches Hilfsmittel zusammengebaut haben, das auf einer Textdatei beruht.
Das nächste Beispiel nutzt eine Datei mit drei Feldern, die Sie auf vielerlei Art verarbeiten können. Die Datei mit dem Namen areacodes.txt enthält Telefonvorwahlen für die Vereinigten Staaten von Amerika. Sie können sie aus dem Zusatzmaterial (https://efficientlinux.com/examples) zu diesem Buch im Verzeichnis chapter09/build_area_code_database beziehen oder Ihre eigene Datei anlegen, etwa aus der Wikipedia (https://oreil.ly/yz2M1):2
201 NJ Hackensack, Jersey City
202 DC Washington
203 CT New Haven, Stamford
...
989 MI Saginaw
Tipp Ordnen Sie die Felder mit den vorhersehbaren Längen zuerst an, sodass die Spalten ordentlich aussehen. Schauen Sie einmal, wie unordentlich die Datei wirkt, wenn Sie die Städtenamen in die erste Spalte setzen: |
|
|
Hackensack, Jersey City 201 NJ Washington 202 DC ... |
Wenn diese Datei erst einmal an Ort und Stelle ist, können Sie eine Menge damit anfangen. Suchen Sie Vorwahlen mit grep anhand des Staats heraus. Fügen Sie dabei die Option -w hinzu, um nur vollständige Wörter zu erfassen (falls anderer Text zufällig »NJ« enthält):
$ grep -w NJ areacodes.txt
201 NJ Hackensack, Jersey City
551 NJ Hackensack, Jersey City
609 NJ Atlantic City, Trenton, southeast and central west
...
oder schlagen Sie Städte anhand der Vorwahlen:
$ grep -w 202 areacodes.txt
202 DC Washington
oder nach irgendeinem String in der Datei nach:
$ grep Washing areacodes.txt
202 DC Washington
227 MD Silver Spring, Washington suburbs, Frederick
240 MD Silver Spring, Washington suburbs, Frederick
...
Zählen Sie die Vorwahlen mit wc:
$ wc -l areacodes.txt
375 areacodes.txt
Suchen Sie den Staat mit den meisten Vorwahlen (Gewinner ist Kalifornien mit 38):
$ cut -f2 areacodes.txt | sort | uniq -c | sort -nr | head -n1
38 CA
Konvertieren Sie die Datei in das CSV-Format, um sie in eine Tabellenkalkulation zu importieren. Geben Sie das dritte Feld in doppelte Anführungszeichen eingeschlossen aus, um zu verhindern, dass seine Kommata als CSV-Trennzeichen interpretiert werden:
$ awk -F'\t' '{printf "%s,%s,\"%s\"\n", $1, $2, $3}' areacodes.txt \
> areacodes.csv
$ head -n3 areacodes.csv
201,NJ,"Hackensack, Jersey City"
202,DC,"Washington"
203,CT,"New Haven, Stamford"
Fassen Sie alle Vorwahlen für einen bestimmten Staat auf einer einzigen Zeile zusammen:
$ awk '$2~/^NJ$/{ac=ac FS $1} END {print "NJ:" ac}' areacodes.txt
NJ: 201 551 609 732 848 856 862 908 973
oder fassen Sie sie für jeden Staat zusammen, wobei Sie Arrays und for-Schleifen nutzen wie in »Den Duplikate-Detektor verbessern« auf Seite 109:
$ awk '{arr[$2]=arr[$2] " " $1} \
END {for (i in arr) print i ":" arr[i]}' areacodes.txt \
| sort
AB: 403 780
AK: 907
AL: 205 251 256 334 659
...
WY: 307
Verwandeln Sie die gerade gezeigten Befehle je nach Vorliebe in Aliase, Funktionen oder Skripte. Ein einfaches Beispiel ist das Skript areacode in Beispiel 9-4.
Beispiel 9-4: Das Skript areacode
#!/bin/bash
if [ -n "$1" ]; then
grep -iw "$1" areacodes.txt
fi
Das Skript areacode sucht nach ganzen Wörtern in der Datei areacodes.txt, wie etwa der Vorwahl, der Abkürzung des Staats oder dem Namen der Stadt:
$ areacode 617
617 MA Boston
Lassen Sie uns in einem letzten, ausführlichen Beispiel Benutzernamen, Passwörter und Notizen in einer verschlüsselten Textdatei abspeichern, und zwar in einem strukturierten Format für eine einfache Abfrage auf der Kommandozeile. Der resultierende Befehl ist ein einfacher Passwortmanager – eine Anwendung, die einem die Last abnimmt, sich viele komplizierte Passwörter merken zu müssen.
Warnung Die Passwortverwaltung ist ein komplexes Thema in der Computersicherheit. Dieses Beispiel erzeugt einen extrem einfachen Passwortmanager zu Übungs- und Lehrzwecken. Verwenden Sie ihn nicht für wichtige Anwendungen mit echtem Sicherheitsbedarf. |
Die Passwortdatei mit dem Namen vault besitzt drei Felder, die jeweils durch ein Tab-Zeichen getrennt sind:
Erzeugen Sie die Datei vault und fügen Sie die Daten hinzu. Die Datei ist noch nicht verschlüsselt, sodass Sie momentan nur unechte Passwörter einfügen sollten:
$ touch vault Erzeugt eine leere Datei.
$ chmod 600 vault Setzt die Dateiberechtigungen.
$ emacs vault Bearbeitet die Datei.
$ cat vault
sally fake1 google.com account
ssmith fake2 dropbox.com account for work
s999 fake3 Bank of America account, bankofamerica.com
smith2 fake4 My blog at wordpress.org
birdy fake5 dropbox.com account for home
Speichern Sie vault an einem bekannten Ort:
$ mkdir ~/etc
$ mv vault ~/etc
Die Idee ist, ein Pattern-Matching-Programm wie grep oder awk zu benutzen, um Zeilen auszugeben, die einem bestimmten String entsprechen. Diese einfache, aber leistungsstarke Technik kann jeden Teil einer Zeile erfassen, nicht nur Benutzernamen oder Websites, zum Beispiel:
$ cd ~/etc
$ grep sally vault Erfasst einen Benutzernamen.
sally fake1 google.com account
$ grep work vault Erfasst die Anmerkungen.
ssmith fake2 dropbox.com account for work
$ grep drop vault Erfasst mehrere Zeilen.
ssmith fake2 dropbox.com account for work
birdy fake5 dropbox.com account for home
Halten Sie diese einfache Funktionalität in einem Skript fest. Anschließend wollen wir sie Schritt für Schritt verbessern und die Datei vault am Ende verschlüsseln. Nennen Sie das Skript pman für Passwort Manager und erzeugen Sie die einfache Version aus Beispiel 9-5.
Beispiel 9-5: pman, Version 1: So einfach es geht
#!/bin/bash
# Einfach nur passende Zeilen ausgeben.
grep "$1" $HOME/etc/vault
Speichern Sie das Skript in Ihrem Suchpfad:
$ chmod 700 pman
$ mv pman ~/bin
Probieren Sie das Skript aus:
$ pman goog
sally fake1 google.com account
$ pman account
sally fake1 google.com account
ssmith fake2 dropbox.com account for work
s999 fake3 Bank of America account, bankofamerica.com
birdy fake5 dropbox.com account for home
$ pman facebook (erzeugt keine Ausgabe)
Die nächste Version in Beispiel 9-6 enthält dann schon eine kleine Fehlerüberprüfung und einige sinnvolle Variablennamen.
Beispiel 9-6: pman, Version 2: Fügt eine Fehlerüberprüfung hinzu
#!/bin/bash
# Festhalten des Skriptnamens.
# $0 ist der Pfad auf das Skript, und basename gibt den endgültigen Dateinamen aus.
PROGRAM=$(basename $0)
# Ort der Passwortdatei vault.
DATABASE=$HOME/etc/vault
# Sicherstellen, dass wenigstens ein Argument an das Skript übergeben wurde.
# Der Ausdruck >&2 leitet echo auf die Standardfehlerausgabe um
# statt auf die Standardausgabe.
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: Passwörter nach String suchen"
>&2 echo "Verwendung: $PROGRAM String"
exit 1
fi
# Speichert das erste Argument in einer freundlichen, benannten Variablen.
searchstring="$1"
# Durchsucht vault und gibt eine Fehlermeldung aus, falls nichts passt.
grep "$searchstring" "$DATABASE"
if [ $? -ne 0 ]; then
>&2 echo "$PROGRAM: keine Treffer für '$searchstring'"
exit 1
fi
$ pman
pman: Passwörter nach String suchen
Verwendung: pman String
$ pman smith
ssmith fake2 dropbox.com account for work
smith2 fake4 My blog at wordpress.org
$ pman xyzzy
pman: keine Treffer für 'xyzzy'
Ein Nachteil dieser Technik besteht darin, dass sie nicht skaliert. Wenn vault Hunderte von Zeilen enthält und grep 63 von ihnen erfasst und ausgibt, müssen Sie diese »durch scharfes Hinschauen« durchsuchen, um das Passwort zu finden, das Sie brauchen. Verbessern Sie das Skript, indem Sie einen einmaligen Schlüssel (einen String) zu jeder Zeile in der dritten Spalte hinzufügen und pman so anpassen, dass es zuerst nach diesem einmaligen Schlüssel sucht. Die Datei vault sieht nun so aus (die dritte Spalte wurde fett gedruckt):
sally fake1 google google.com account
ssmith fake2 dropbox dropbox.com account for work
s999 fake3 bank Bank of America account, bankofamerica.com
smith2 fake4 blog My blog at wordpress.org
birdy fake5 dropbox2 dropbox.com account for home
Beispiel 9-7 zeigt das aktualisierte Skript, das awk anstelle von grep einsetzt. Außerdem verwendet es eine Befehlssubstitution, um die Ausgabe zu erfassen und zu überprüfen, ob sie leer ist (der Test -z bedeutet »String der Länge null«). Falls Sie übrigens nach einem Schlüssel suchen, der in vault nicht existiert, fällt pman in sein ursprüngliches Verhalten zurück und gibt alle Zeilen aus, die dem Suchstring entsprechen.
Beispiel 9-7: pman, Version 3: Zuerst wird nach dem Schlüssel in der dritten Spalte gesucht
#!/bin/bash
PROGRAM=$(basename $0)
DATABASE=$HOME/etc/vault
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: Passwörter suchen"
>&2 echo "Verwendung: $PROGRAM String"
exit 1
fi
searchstring="$1"
# Suchen nach exakten Treffern in der dritten Spalte.
match=$(awk '$3~/^'$searchstring'$/' "$DATABASE")
# Falls der Suchstring keinem Schlüssel entspricht, werden alle Treffer gesucht.
if [ -z "$match" ]; then
match=$(awk "/$searchstring/" "$DATABASE")
fi
# Falls es immer noch keinen Treffer gibt, wird eine Fehlermeldung ausgegeben,
# und das Skript wird beendet.
>&2 echo "$PROGRAM: keine Treffer für '$searchstring'"
exit 1
fi
# Ausgeben des Treffers.
echo "$match"
Führen Sie das Skript aus:
$ pman dropbox
ssmith fake2 dropbox dropbox.com account for work
$ pman drop
ssmith fake2 dropbox dropbox.com account for work
birdy fake5 dropbox2 dropbox.com account for home
Die einfache Textdatei vault ist ein Sicherheitsrisiko. Verschlüsseln Sie sie deshalb mit dem Linux-Standardverschlüsselungsprogramm GnuPG, das als gpg aufgerufen wird. Falls Sie GnuPG bereits für den Einsatz eingerichtet haben, ist das großartig. Falls nicht, richten Sie es mit dem folgenden Befehl ein, bei dem Sie Ihre E-Mail-Adresse angeben:3
$ gpg --quick-generate-key Ihre_E-Mail-Adresse default default never
Sie werden nach einer Passphrase für den Schlüssel gefragt (zweimal). Geben Sie eine starke Passphrase an. Wenn gpg fertig ist, können Sie die Passwortdatei mit einer Public-Key-Verschlüsselung (Verschlüsselung mit öffentlichem Schlüssel) verschlüsseln. Dabei entsteht die Datei vault.gpg:
$ cd ~/etc
$ gpg -e -r Ihre_E-Mail-Adresse vault
$ ls vault*
vault vault.gpg
Entschlüsseln Sie als Test die Datei vault.gpg auf die Standardausgabe:4
$ gpg -d -q vault.gpg
Passphrase: xxxxxxxx
sally fake1 google google.com account
ssmith fake2 dropbox dropbox.com account for work
...
Aktualisieren Sie dann Ihr Skript, sodass es die verschlüsselte Datei vault.gpg statt der einfachen Textdatei vault verwendet. Das bedeutet ein Entschlüsseln von vault.gpg auf die Standardausgabe und ein Weiterleiten seines Inhalts in einer Pipeline an awk für einen Vergleich, wie in Beispiel 9-8 gezeigt.
Beispiel 9-8: pman, Version 4: Verwenden einer verschlüsselten vault-Datei
#!/bin/bash
PROGRAM=$(basename $0)
# Use the encrypted file.
DATABASE=$HOME/etc/vault.gpg
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: Passwörter suchen"
>&2 echo "Verwendung: $PROGRAM String"
exit 1
fi
searchstring="$1"
# Speichern des entschlüsselten Texts in einer Variablen.
decrypted=$(gpg -d -q "$DATABASE")
# Suchen nach exakten Treffern in der dritten Spalte.
match=$(echo "$decrypted" | awk '$3~/^'$searchstring'$/')
# Falls der Suchstring nicht zu einem Schlüssel passt, werden alle Treffer gesucht.
if [ -z "$match" ]; then
match=$(echo "$decrypted" | awk "/$searchstring/")
fi
# Falls es immer noch keinen Treffer gibt, wird eine Fehlermeldung ausgegeben,
# und das Skript wird beendet.
if [ -z "$match" ]; then
>&2 echo "$PROGRAM: keine Treffer für '$searchstring'"
exit 1
fi
# Ausgeben des Treffers
echo "$match"
Das Skript zeigt nun die Passwörter aus der verschlüsselten Datei an:
$ pman dropbox
Passphrase: xxxxxxxx
ssmith fake2 dropbox dropbox.com account for work
$ pman drop
Passphrase: xxxxxxxx
ssmith fake2 dropbox dropbox.com account for work
birdy fake5 dropbox2 dropbox.com account for home
Es sind nun alle Teile für Ihren Passwortmanager vorhanden. Ein paar letzte Schritte fehlen aber noch:
decrypted=$(gpg -d -q "$DATABASE" | grep -v '^#')
Das Ausgeben von Passwörtern auf der Standardausgabe ist aus Sicht der Sicherheit keine so großartige Idee. »Den Passwortmanager verbessern« auf Seite 208 passt das Skript so an, dass Passwörter kopiert und eingefügt statt ausgegeben werden.
Verschlüsselte Dateien direkt bearbeiten
Um eine verschlüsselte Datei zu verändern, wäre die direkteste, lästigste und unsicherste Methode das Entschlüsseln, Bearbeiten und erneute Verschlüsseln der Datei.
$ cd ~/etc
$ gpg vault.gpg Entschlüsseln.
Passphrase: xxxxxxxx
$ emacs vault Benutzen Sie Ihren Lieblingstexteditor.
$ gpg -e -r Ihre_E-Mail-Adresse vault Selbst wieder verschlüsseln.
$ rm vault
Zum einfacheren Bearbeiten der Datei vault.gpg besitzen sowohl emacs als auch vim Modi zum Bearbeiten GnuPG-verschlüsselter Dateien. Fügen Sie zuerst diese Zeile einer bash-Konfigurationsdatei hinzu und laden Sie sie in allen damit verbundenen Shells neu:
export GPG_TTY=$(tty)
Für emacs richten Sie das EasyPG-Paket ein, das eingebaut ist. Fügen Sie die folgenden Zeilen der Konfigurationsdatei $HOME/.emacs hinzu und starten Sie emacs neu. Ersetzen Sie den String GnuPG ID here durch die E-Mail-Adresse, die mit Ihrem Schlüssel verknüpft ist, also etwa smith@example.com:
(load-library "pinentry")
(setq epa-pinentry-mode 'loopback)
(setq epa-file-encrypt-to "GnuPG ID here")
(pinentry-start)
Bearbeiten Sie dann die verschlüsselte Datei. emacs fragt Sie nach Ihrer Passphrase und entschlüsselt die Datei zur Bearbeitung in einen Puffer. Beim Speichern verschlüsselt emacs den Inhalt des Puffers.
Für vim nutzen Sie das Plug-in vim-gnupg (https://oreil.ly/mnwYc) und fügen diese Zeilen in die Konfigurationsdatei $HOME/.vimrc ein:
let g:GPGPreferArmor=1
let g:GPGDefaultRecipients=["GnuPG ID here"]
Sie sollten sich überlegen, einen Alias anzulegen, um die Passwortdatei bequem bearbeiten zu können. Verwenden Sie dazu die Technik aus dem Abschnitt »Häufig bearbeitete Dateien mit einem Alias bearbeiten« auf Seite 72:
alias pwedit="$EDITOR $HOME/etc/vault.gpg"
Dateipfade, Domainnamen, Vorwahlen und Login-Daten sind nur einige Beispiele für Daten, die gut in einer strukturierten Textdatei funktionieren. Wie wäre es mit:
Auf diese Weise können Sie sich ein Ökosystem aus zeitsparenden Befehlen zusammenstellen, die für Sie persönlich bedeutsam oder für Ihre Arbeit wichtig sind. Ihrer Fantasie sind hier keine Grenzen gesetzt.