Die akzeptierten / hoch bewerteten Antworten sind großartig, aber es fehlen ein paar Details. Dieser Beitrag behandelt die Fälle, wie Sie besser damit umgehen können, wenn die Erweiterung des Shell-Pfadnamens (Glob) fehlschlägt, wenn Dateinamen eingebettete Zeilenumbrüche / Strichsymbole enthalten und die Umleitung der Befehlsausgabe aus der for-Schleife verschoben wird, wenn die Ergebnisse in a geschrieben werden Datei.
Wenn Sie die Shell-Glob-Erweiterung mit ausführen, besteht die *
Möglichkeit, dass die Erweiterung fehlschlägt, wenn keine Dateien im Verzeichnis vorhanden sind und eine nicht erweiterte Glob-Zeichenfolge an den auszuführenden Befehl übergeben wird, was zu unerwünschten Ergebnissen führen kann. Die bash
Shell bietet hierfür eine erweiterte Shell-Option nullglob
. Die Schleife wird also im Grunde genommen wie folgt in dem Verzeichnis, das Ihre Dateien enthält
shopt -s nullglob
for file in ./*; do
cmdToRun [option] -- "$file"
done
Auf diese Weise können Sie die for-Schleife sicher verlassen, wenn der Ausdruck ./*
keine Dateien zurückgibt (wenn das Verzeichnis leer ist).
oder auf POSIX-konforme Weise ( nullglob
ist bash
spezifisch)
for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done
Auf diese Weise können Sie in die Schleife gehen, wenn der Ausdruck einmal fehlschlägt, und die Bedingung [ -f "$file" ]
überprüfen, ob die nicht erweiterte Zeichenfolge ./*
ein gültiger Dateiname in diesem Verzeichnis ist, was nicht der Fall wäre. Wenn diese Bedingung fehlschlägt, kehren continue
wir zu der for
Schleife zurück, die anschließend nicht ausgeführt wird.
Beachten Sie auch die Verwendung von --
kurz vor dem Übergeben des Dateinamenarguments. Dies ist erforderlich, da die Shell-Dateinamen, wie bereits erwähnt, an beliebiger Stelle im Dateinamen Bindestriche enthalten können. Einige der Shell-Befehle interpretieren dies und behandeln sie als Befehlsoption, wenn der Name nicht richtig in Anführungszeichen gesetzt wird, und führen den Befehl aus, wenn das Flag bereitgestellt wird.
Das --
signalisiert in diesem Fall das Ende der Befehlszeilenoptionen. Dies bedeutet, dass der Befehl keine Zeichenfolgen über diesen Punkt hinaus als Befehlsflags, sondern nur als Dateinamen analysieren sollte.
Das doppelte Zitieren der Dateinamen löst die Fälle, in denen die Namen Glob-Zeichen oder Leerzeichen enthalten. * Nix-Dateinamen können aber auch Zeilenumbrüche enthalten. Daher beschränken wir Dateinamen mit dem einzigen Zeichen, das nicht Teil eines gültigen Dateinamens sein kann - dem Null-Byte ( \0
). Da bash
intern Stilzeichenfolgen C
verwendet werden, in denen die Nullbytes verwendet werden, um das Ende der Zeichenfolge anzugeben, ist dies der richtige Kandidat dafür.
So mit der printf
die Möglichkeit , Shell abgrenzen Dateien mit diesem NULL - Byte mit -d
Option read
Befehl können wir tun unten
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
Die nullglob
und die printf
werden umbrochen, (..)
was bedeutet, dass sie im Grunde genommen in einer Unter-Shell (untergeordnete Shell) ausgeführt werden, da nach dem nullglob
Beenden des Befehls die Option vermieden wird, über die übergeordnete Shell nachzudenken. Die -d ''
Befehlsoption read
ist nicht POSIX-kompatibel und benötigt daher eine bash
Shell, um dies zu tun. Mit dem find
Befehl kann dies wie folgt erfolgen
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
Für find
Implementierungen, die nicht unterstützt werden -print0
(außer den GNU- und FreeBSD-Implementierungen), kann dies mit emuliert werdenprintf
find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --
Ein weiterer wichtiger Fix besteht darin, die Neuausrichtung aus der for-Schleife zu verschieben, um eine hohe Anzahl von Datei-E / A zu reduzieren. Bei Verwendung innerhalb der Schleife muss die Shell Systemaufrufe zweimal für jede Iteration der for-Schleife ausführen, einmal zum Öffnen und einmal zum Schließen des der Datei zugeordneten Dateideskriptors. Dies wird zu einem Engpass für Ihre Leistung beim Ausführen großer Iterationen. Empfohlener Vorschlag wäre, es außerhalb der Schleife zu verschieben.
Sie können den obigen Code mit diesen Korrekturen erweitern
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out
Dadurch wird der Inhalt Ihres Befehls für jede Iteration Ihrer Dateieingabe auf stdout gesetzt. Wenn die Schleife endet, öffnen Sie die Zieldatei einmal, um den Inhalt des stdout zu schreiben und zu speichern. Die äquivalente find
Version davon wäre
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out