Mac Perl Primer

Dateien umbenennen

Wir stellen uns folgendes Szenario vor: einer unserer Kollegen hat einen PC. Er gibt uns eine Diskette mit seiner Sammlung von Html-Seiten. Wir öffnen sie mit unseren Mac und freuen uns, das wir die Dateien alle lesen können. Leider sind alle Dateinamen in Großbuchstaben. Nachdem wir die ersten 5 Dateien per Hand umbenannt haben, überlegen wir ob es nicht einen besseren Weg gibt.

Ein paar Zeilen Perl lösen das Problem:

#!perl -w
# lowerfilename.pl: take all filenames and convert to lowercase
# (taken from book: wall/schwartz: programming in perl)
 
for (@ARGV) 
{
	$was = $_;
	tr/A-ZÄÖÜ/a-zäöü/;
	rename( $was, $_ ) unless $was eq $_;
}

Wir sichern dieses kleine Skript in Perl als Droplet. Dann wechseln wir in den Finder und ziehen ein paar Dateien auf das Droplet. Wenn die Dateien Großbuchstaben im Namen enthalten, werden diese in Kleinbuchstaben umgewandelt. Wir können übrigens mit Hilfe von "Shift-Klick" mehrere Dateien auswählen und gemeinsam auf das Droplet ziehen, sie werden alle in einem Rutsch umbenannt.

Wie funktionieren jetzt diese Zeilen? Die spezielle Variable @ARGV enthält alle "Kommandozeilenparameter". So etwas haben wir zwar auf den Mac natürlich nicht. Aber wenn wir unser Skript als Droplett speichern, so enthält @ARGV die Dateinamen aller Dateien, die wir auf das Droplett gezogen haben. (Genauer Dateinamen mit Pfad, aber dazu später mehr).

Die for Anweisung sorgt dafür, daß alles innerhalb der geschweiften Klammer für jedes Element der Liste @ARGV einmal ausgeführt wird. Das Element, also unser Dateiname, wird in der Defaultvariablen $_ gespeichert. Als erstes merken wir uns diesen Dateinamen in der Variablen $was.

Jetzt wandeln wir den Namen um: Hierfür dient der tr/../../ Befehl. tr steht für translate. Alle Zeichen zwischen den ersten beiden Schrägstrichen werden durch die enstprechenden Zeichen zwischen zweiten und dritten Schrägstrich ersetzt. Weil kaum jemand Lust hat alle Buchstaben einzeln auszuführen gibt es die Abkürzung A-Z bzw. a-z. Der tr Befehl wirkt in seiner einfachsten Form auf die Defaultvariable $_.

Unser $_ enthält also jetzt den Dateinamen in Kleinbuchstaben. Wichtig ist es nun sich klarzumachen, daß die Datei selbst immer noch den alten Namen hat, während $_ und auch @ARGV nur Zeichenketten enthalten, die mit den Dateinamen übereinstimmen. Wir müssen jetzt also der Datei selbst noch den neuen Namen geben. Hierfür gibt es die rename() Funktion. Sie wird mit zwei Argumenten aufgerufen, das erste gibt an welche Datei umbenannt wird und das zweite wie die Datei jetzt heißen soll.

Um den Dateisystem unnötige Arbeit zu ersparen, sorgen wir dafür das die Datei nur umbenannt wird wenn der neue Name wirklich verschieden vom alten Namen ist. Der Vergleich geschieht mit dem Konstrukt $was eq $_. Wichtig ist es zwischen den beiden Operatoren eq und == zu unterscheiden. eq testet ob die Zeichenketten gleich sind, während == testet ob Zahlen gleich sind. Da Zahlen und Zeichenketten in Perl beliebig und automatisch umgewandelt werden kann man hier leicht einen Fehler machen. Nehmen wir folgendes Beispiel:

"012" == "12"  # liefert wahr, weil Zahlen gleich sind
"012" eq "12"  # liefert falsch, weil Zeichen nicht identisch sind
 

Es bleibt noch die Bedeutung des unless zu erklären. A unless B bedeutet "führe A aus , es sei denn B ist wahr". Man kann die Anweisung auch herkömmlich mit einem vorangestellten if kodieren:

if( $was ne $_)
{
    rename( $was, $_ );
}

Probleme

Unser kleines Skript hat noch zwei Probleme: Zum einen wurde oben schon angedeutet, das nicht nur die Dateinamen, sondern die kompletten Pfade verarbeitet werden. Zum Anderen gibt es eine Grenze der in einem Rutsch zu verarbeitenden Dateien: Bei mehreren Hundert oder Tausend kann es sein das nicht alles vararbeitet wird. Die genaue Grenze hängt von der verwendeten Version des MacOS ab.

Das erste Problem stört in unserem speziellen Anwendungsfall erstaunlicherweise nicht. Wir können das Problem untersuchen, indem wir statt des rename Befehls eine print Befehl einfügen. So sehen wir was Perl sieht:

for (@ARGV) 
{
	$was = $_;
	tr/A-ZÄÖÜ/a-zäöü/;
	print "$was -> $_\n";
}

Es ist übrigens ratsam, alle Skripte die Dateien umbenennen, löschen, kopieren etc. zunächst mit print Befehlen zu testen, ob Perl auch wirklich das tut was man sich vorstellt. In unserem Beispiel sehen wir, das nicht nur der Dateiname, sondern auch der Name der Festplatte und der Ordner, in denen die Datei enthalten ist in Kleinbuchstaben umwandeln möchte.

Wenn wir nun eine Datei haben, die auf der Festplatte "Macintosh HD" liegt, müßte unser Skript eigentlich auch die Festplatte selbst in "macintosh hd" umbenennen. Zum Glück passiert dies nicht, zumindestens nicht auf meinen Rechner. Aber ein besseres Vorgehen wäre den langen Pfadnamen zu zerlegen und nur den Dateinamen in Kleinbuchstaben umzuwandeln. Wie so oft bei MacPerl gibt es schon ein Modul, was einen bei dieser Aufgabe hilft:

use File::Basename;
for (@ARGV) 
{
    ($name, $path) = &fileparse($_);
	$oldname = $name;
	$name =~ tr/A-ZÄÖÜ/a-zäöü/;
	rename( $oldname, $_ );
}

Ende des MacPerl Primers.

<- vorherige Lektion


home - contact - dev-zone