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 Bearbeiten

Brauchbar 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:

  1. 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.
  2. 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 Bearbeiten

Das 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 Bearbeiten

Zuerst 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 Bearbeiten

Das 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 Bearbeiten

Verlasse 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 Bearbeiten

Jetzt 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 ... nichtz. 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 Bearbeiten

Etwas 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 Bearbeiten

Das 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 Bearbeiten

Das 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