Eine interessante kleine Aufgabenstellung: ein Skript hat versehentlich tausende von Dateien mit einer verkehrten Extension generiert. Alle Dateien heissen .jpgjpg statt einfach nur .jpg. Gestandene Shell-Benutzer reparieren sowas natürlich mit einem formschönen Einzeiler. Das ist einfach, und eigentlich lohnt es sich nicht da lange drüber nachzudenken. Es sei denn man kommt auf die Idee, das zeitlich zu optimieren…

Im konkreten Fall — es geht um etwa 6000 Dateien — dauert die Optimierung länger als das was an Laufzeitgewinn rauskommt. Wenn wir also schon keine Zeit gewinnen, dann wenigstens Erkenntnis. :-)

Also, mein erster Ansatz ist es intuitiv, alle Dateien zu suchen, für jede Datei den neuen Namen festzulegen und dann umzubenennen. Machen wir eine Trockenübung bei laufender Stopuhr:

Das führt zu einer Laufzeit von erstaunlichen 21,443 Sekunden!

Halbwegs moderne Shells wie die Bash beherrschen Stringmanipulation, dazu muss man nicht zwingend auf sed zurückgreifen. Im TLDP steht wie es geht, so finde ich dort unter ‚Substring Removal‘ den richtigen Ansatz:

Das drückt die Zeit für die gleichen Testdaten schon auf 0,811 Sekunden. Soweit ich weiss haben wir uns damit zwar einen Bashismus eingehandelt, aber das ist heutzutage vielleicht schon egal.

Wenn wir aber schon Kompatibilität aufgeben können wir — schliesslich sind wir in einer hochmodernen Zsh — gleich in die Vollen gehen:

Ich habe noch nie rekursives Globbing (die zsh-lovers-Manpage hat mir verraten wie das geht) benutzt, aber nach nur 0,165 Sekunden war klar: es funktioniert. :-)

Oh, da man hier auch denken könnte dass das Betriebssystem den Dateibaum cached: nein, das ist nicht so. Wenn ich die Einzeiler mehrfach absetze, auch in anderen Reihenfolgen, kommen immer wieder vergleichbare Ergebnisse raus.

Da die effizienteste Methode auch gleichzeitig die am schnellsten zu tippende ist denke ich, dass die — wenn ich auswendig gewusst hätte wie das geht — in jeder Hinsicht die erste Wahl gewesen wäre. Aber ich kenne mich: wenn ich das ein paar Tage nicht benutze muss ich erst wieder recherchieren. Und dann falle ich doch wieder auf das bewährte sed zurück…

Vor einer Weile habe ich auf Twitter einen netten Alias für die Shell gesehen:

alias doch='sudo $(history -p !-1)'

Das Ding geht nicht in der zsh, da das history-Kommando — anders als in der bash — hier kein p-Flag hat. Ist aber auch zweitrangig. Wenn man sein letztes Kommando doch mal mit Nachdruck (und root-Rechten) ausführen möchte geht auch ein beherztes:

sudo !!

An dieser Stelle nochmal ein Zitat zum Thema:

‚Multiple exclamation marks,‘ he went on, shaking his head, ‚are a sure sign of a diseased mind.‘
(Terry Pratchett in „Eric“)

Ich glaube auch Herr Pratchett würde die Anwendung mit sudo als Ausnahme durchgehen lassen, oder? :-)

Zwischendurch mal was nützliches für alle die — wie ich — viel in Unix-Shells unterwegs sind. Insbesondere für die die — wie ich — auch gerne mal ‚Einzeiler‘ schreiben die über mehrere Bildschirmzeilen gehen… :-)

Das Kommando fc steht für ‚fix command‘, damit wird die zuletzt ausgeführte Zeile zur Bearbeitung im Editor geöffnet. Also idealerweise im Vim. Da kann man komfortabel seine Änderungen vornehmen, direkt nach Beendigung des Editors wird das Kommando ausgeführt. Zumindest die Bash und die Zsh (letztere ist die interaktive Shell meiner Wahl) können das.

Ich persönlich kannte das vorher noch nicht. Hilfreich wäre es schon in wirklich vielen Situationen gewesen: ich neige wie gesagt dazu komplexe Shell-Zeilen zusammenzubauen. Wenn ich mit einem Ergebnis zufrieden bin schiebe ich es oft mittels echo in eine Datei, um die dann zum Shellskript umzuformen. Wenn ich mit fc eh in den Editor wechsele kann ich nicht nur von vornherein sauberer schreiben, sondern bei Bedarf mit ‚:w tollesskript.sh‘ direkt in eine Datei sichern.