Linux-Workshop

Shell Scripting im Admin-Alltag

Datei-Handling

Hin und wieder erhalten wir nachfolgende Meldung, wenn es darum geht, eine hohe Zahl an Dateien zu Durchsuchen oder Logdateien zu Löschen:

> ls *.gz

-bash: /bin/ls: Argument list too long

Die einem Prozess übergebene Zahl an Argumenten wurde überschritten. Wenn die Option "noglob" in der Shell deaktiviert ist (default; set +-f; Anzeige: set -o), dann wird der Ausdruck "*.gz" so expandiert, dass alle Dateien mit der Endung ".gz" im aktuellen Verzeichnis gesucht, und dem Befehl "ls" als Argumentenliste übergeben werden. Effektiv führt die Shell daher den Aufruf "ls file1.gz file2.gz .. fileN.gz" aus, wenn diese Dateien vorhanden sind.

Würden wir das so genannten Globbing, das heißt die Expansion von "*?" durch die Shell, mittels "set -f" deaktivieren, dann hat "*" für die Shell keine besondere Bedeutung mehr.

> set -f

> zgrep test *.gz

gzip: *.gz: No such file or directory

> touch /tmp/*.gz /tmp/a.gz

> ls -l /tmp/*.gz

-rw-rw-r-- 1 user grp 0 Dec 7 10:55 /tmp/*.gz

Globbing wieder aktiv:

> set +f

> ls -l /tmp/*.gz

-rw-rw-r-- 1 user grp 0 Dec 7 10:57 /tmp/a.gz

-rw-rw-r-- 1 user grp 0 Dec 7 10:55 /tmp/*.gz

Unserem Befehl "ls/ zgrep" müssen wir daher die Dateien häppchenweise servieren.

Möglichkeiten:

ls [a-h]*.gz ; zgrep test [i-n]*.gz; ls [o-z].gz

find . -type f -name '*.gz' -exec zgrep test {} \;

find . -type f -name '*.gz' | xargs -n 200 zgrep test

Unter der Annahme, dass die jeweilige Expansion von "[a-h]*.gz", "[i-n]*.gz" und "[o-z]*.gz" die maximale Anzahl an Argumenten nicht überschreitet, wird jeweils nur ein Teil der Dateien dem Aufruf bei Option 1. übergeben.

Bei Option 2. expandiert der Befehl "find" den Ausdruck ‚*.gz’ selbst, daher ist es ein Unterschied, ob wir "*.gz" oder "*.gz" übergeben. Im ersten Fall wird auch hier wieder die Shell aktiv und expandiert diesen Ausdruck, was wir nicht möchten. Die Option 1. automatisiert ergibt den Aufruf bei Möglichkeit 3. Hier liest "xargs" maximal 200 Dateinamen über stdin ein, führt mit dieser Liste das nachfolgende Kommando aus. Dies wiederholt sich, bis alle Namen verarbeitet wurden.

Wenn die Dateinamen aber Leerzeichen und/oder Zeilenumbruch enthalten, dann empfiehlt sich im Fall 3. die folgende Syntax, die den Wert 0 als Dateiseparator, statt Leerzeichen (ASCII-32) verwendet:

> find . -type f -name '*.gz' -print0 | xargs -0n 200 zgrep test

Ab Kernel Version 2.6.23 sollte diese Meldung nicht mehr sobald erscheinen, denn der Kernel wurde so angepasst, dass die Anzahl der Argumente nun dynamisch angepasst wird (zirka 25% von ulimit -s; der Prozessstack-Grösse).

Kernel 2.6.18:

> ls *.gz

-bash: /bin/ls: Argument list too long

> find . -name '*.gz' |wc -l

6922

Kernel 2.6.35:

> find . -name 'test*' | wc -l

150000