Linux-Praxisbuch/ Skripte
Es gibt Arbeiten, die man immer wieder machen muss und die immer dieselben bleiben. Zum Beispiel ein Backup durchführen, die Berechtigungen aller Dateien in einem Verzeichnis auf einen bestimmten Benutzer ändern oder in einem Text Fehler korrigieren. Diese Aufgaben können in ein Skript geschrieben werden, ähnlich den Batch-Dateien unter Windows. Skripte bekommen normalerweise die Endung .sh.
Wikipedia-Text mit sed korrigieren
BearbeitenBrauchbar sind Beispiele nur dann, wenn man sie auch anwenden kann :) Das Ziel ist ein Skript, das häufige kleine typografische Fehler beseitigt. Zuerst sind das nur folgende:
- z.B. zu z. B. (im Quellcode: z. B.)
- Zwischen den beiden Buchstaben gehört ein geschütztes Leerzeichen. Bei einem normalen könnte z.
B. das hier geschehen. hält die beiden Zeichen aber zusammen.
- Zwischen den beiden Buchstaben gehört ein geschütztes Leerzeichen. Bei einem normalen könnte z.
- Richtige Gedankenstriche: – statt - (im Quellcode: –)
- Gedankenstriche haben vorher und nachher einen Leerschlag – und sind so lange wie ein n, darum werden sie als – geschrieben.
Das soll das Skript mit allen Dateien im Verzeichnis machen, die auf .wiki enden.
Für die Umsetzung verwenden wir sed, einen automatisierten Texteditor, mit dem man unter anderem sehr leicht bestimmte Textpassagen finden und ersetzen kann.
Das Skript anlegen
BearbeitenDas Skript kann mit irgendeinem Reintext-Editor geschrieben werden, beispielsweise mit vi/vim.
vim typo-korr.sh
erstellt die Datei typo-korr.sh und öffnet sie. (Wie du in vim arbeitest erklärt dir das oben genannte Tutorial oder das vim-eigene Tutorial, das du mit dem Befehl vimtutor aufrufen kannst.
Am Anfang war die Schleife
BearbeitenZuerst brauchen wir eine Schleife, damit alle .wiki-Dateien nacheinander bearbeitet werden. Das ist glücklicherweise unkompliziert:
for I in *.wiki do # hier kommt der Code hin done
Das Zeichen „#“ fügt einen Kommentar ein, alles, was dahinter auf der selben Zeile steht, wird ignoriert. So kann man dem nächsten Programmierer, der das Skript anschaut, sagen, was gemacht wird – oder sich selber. Bei komplizierten und langen Skripten ist das ein Muss.
Einfach mal testen
BearbeitenDas einfachste Beispiel ist nun, einfach alle Dateinamen auszugeben. Bei jedem Durchlauf kommt der nächste auf .wiki endende Dateiname in die Variable „I“, deren Inhalt bekommt man mit einem Dollarzeichen. Mit echo $I wird also der Dateiname ausgegeben.
for I in *.wiki do # Dateiname ausgeben echo $I done
Ausprobieren
BearbeitenVerlasse den Editor. Die .sh-Datei muss nun zuerst ausführbar gemacht werden:
chmod 754 typo-korr.sh
oder
chmod +x typo-korr.sh
erledigt das, die zweite Variante allerdings gerade für alle User. Das Skript kann nun ausgeführt werden:
./typo-korr.sh
Der Punkt verweist auf das aktuelle Verzeichnis; einfach typo-korr.sh ist oft aus Sicherheitsgründen gesperrt. Eine andere Möglichkeit, bei der man die Berechtigungen nicht ändern muss:
sh typo-korr.sh
Der eigentliche Code
BearbeitenJetzt fehlt noch der Code, alles Falsche zu ersetzen. sed kann das. Wir müssen ihm aber zuerst den Dateiinhalt übergeben, und das geht mit cat:
cat $I
gibt im obigen Beispiel den Dateiinhalt jeder auf .wiki endenden Datei aus. Mit dem Pipe-Symbol (|) übergeben wir so den Inhalt sed weiter. Die Wikipedia erklärt dessen Gebrauch:
sed 's/alt/neu/g' Eingabedatei > Ausgabedatei
Die Eingabedatei haben wir ja schon, die wird von cat $I geliefert. „alt“ ist der Suchausdruck, den wir suchen, was für Punkt 1 „z.B.“ ist. „neu“ ist der Text, der stattdessen dort erscheinen soll. Das ist ... nicht „z. B.“! Denn das „&“ steht für den gefundenen Suchausdruck. Dieses Zeichen muss also so markiert werden, dass es als einfaches & interpretiert wird – mit einem Backslash (\):
sed 's/z.B./z.\ B./g'
Als Ausgabedatei geben wir vorsichtshalber $I.new an. Nun sieht das Skript so aus:
for I in *.wiki do cat $I | sed 's/z.B./z.\ B./g' > $I.new echo $I done
Wenn du siehst, dass alles wie gewünscht funktioniert, kannst du das Skript so abändern, dass die Datei auch geschrieben wird. Dazu füge nach echo $I folgende beiden Zeilen hinzu:
rm $I # Alte Datei löschen mv $I.new $I # Neue Datei in alten Namen umbenennen
Jetzt kommt der Bindestrich an die Reihe. Der ist nicht ganz so problemlos. Skripten ist einfach, aber eventuell ist etwas Nacharbeit möglich – wenn zum Beispiel ein Bild einen Namen wie „Matterhorn - die Aussicht.jpg“ hat. Das muss man selbstverständlich manuell zurückändern.
Gesucht ist nun „ - “, was durch „ \– “ ersetzt werden soll. Der Teil ab cat sieht nun so aus:
cat $I | sed -e "s/z.B./z.\ B./g"\ | sed -e "s/ - / \– /g"\ > $I.new
Beachte den Backslash am Zeilenende! Er sagt, dass der Befehl auf der nächsten Zeile weitergeht. Natürlich könnte man alles auf eine Zeile quetschen, aber daran hat niemand Freude.
Fast fertig
BearbeitenEtwas erweitert sieht das fast fertige Skript nun so aus:
for I in *.wiki do cat $I | sed -e "s/z.B./z.\ B./g" \ | sed -e "s/ - / \– /g" \ | sed -e "s/u.a./u.\ a./g"\ | sed -e "s/v.a./v.\ a./g"\ > $I.new echo $I rm $I mv $I.new $I done
Fast fertig! Denn etwas stimmt noch nicht. Die Such- und Ersetzausdrücke sind Reguläre Ausdrücke, das ist etwas vom Spannendsten, was es so gibt. Sogar die Mediawiki-Software arbeitet damit, um die wiki-Tags wie die Eckigen Klammern in Links zu verwandeln. Mach nun folgendes in der Kommandozeile:
echo 'zaBB kuwait vaaa' > regex.wiki ./typobot.sh cat regex.wiki
Das sieht dann etwa so aus:
simon@freezer:~/Skripte$ echo 'zaBB kuwait vaaa' > regex.wiki simon@freezer:~/Skripte$ ./typobot.sh regex.wiki simon@freezer:~/Skripte$ cat regex.wiki z. B. ku. a.t v. a.
Da ist etwas schief gelaufen. Denn: In den Regulären Ausdrücken bedeutet ein Punkt soviel wie „irgend ein Zeichen“, also zum Beispiel ein Punkt oder halt auch ein „w“ oder ein „i“. Nun kommt wieder der Backslash zum Zuge.
Fertig
BearbeitenDas Skript ist nun fertig, du darfst es je nach Belieben erweitern.
for I in *.wiki do cat $I | sed -e "s/z\.B\./z.\ B./g" \ | sed -e "s/ - / \– /g" \ | sed -e "s/u\.a\./u.\ a./g"\ | sed -e "s/v\.a\./v.\ a./g"\ > $I.new echo $I rm $I mv $I.new $I done
Und diesmal kommt das richtige raus:
simon@freezer:~/Skripte$ echo 'zaBB kuwait vaaa' > regex.wiki simon@freezer:~/Skripte$ ./typobot.sh regex.wiki simon@freezer:~/Skripte$ cat regex.wiki zaBB kuwait vaaa
Alles auf einmal
BearbeitenDas Skript kann noch vereinfacht werden, da sed Dateien auch direkt ("in place") bearbeiten kann. Hierfür muss man den Schalter -i angeben. Darüber hinaus kann man mehrere Befehle mit ; (Semikolon) getrennt angeben, die dann auf einmal ausgeführt werden.
Mit diesen beiden Änderungen ergibt sich folgendes Skript:
for I in *.wiki; do sed -i -e "s/z\.B\./z.\ B./g" \ -e "s/ - / \– /g" \ -e "s/u\.a\./u.\ a./g" \ -e "s/v\.a\./v.\ a./g" \ $I echo $I done