Rekursives Durchlaufen von Dateien in einem Verzeichnis

Rekursives Durchlaufen von Dateien in einem Verzeichnis

Das rekursive Durchlaufen der Dateien in einem Verzeichnis ist ganz einfach möglich durch:

find . -type f -exec bar {} \;

Das oben genannte funktioniert jedoch nicht für komplexere Dinge, bei denen viele bedingte Verzweigungen, Schleifen usw. durchgeführt werden müssen. Früher habe ich dies für das Obige verwendet:

while read line; do [...]; done < <(find . -type f)

Allerdings scheint dies bei Dateien mit obskuren Zeichen nicht zu funktionieren:

$ touch $'a\nb'
$ find . -type f
./a?b

Gibt es eine Alternative, die mit solch obskuren Zeichen gut umgehen kann?

Antwort1

Noch eine weitere Verwendung fürsicherfind:

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(Dies funktioniert mit jedem POSIX find, aber der Shell-Teil erfordert Bash. Mit *BSD und GNU find können Sie -print0anstelle von verwenden -exec printf '%s\0' {} +, es ist etwas schneller.)

Dies ermöglicht die Verwendung der Standardeingabe innerhalb der Schleife und funktioniert mitbeliebigWeg.

Antwort2

Dies geht ganz einfach:

find -exec sh -c 'inline script "$0"' {} \;

Oder...

find -exec executable_script {} \;

Antwort3

Der einfachste (und dennoch sichere) Ansatz ist die Verwendung von Shell-Globbing:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

Um das Obige in Unterverzeichnisse rekursiv zu machen (in Bash), können Sie die globstarOption verwenden; auch so eingestellt, dotglobdass es mit Dateien übereinstimmt, deren Name mit beginnt .:

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

Beachten Sie, dass bis Bash 4.2 **/in symbolische Links zu Verzeichnissen rekursiv vorgegangen wird. Seit Bash 4.3 **/wird nur in Verzeichnisse rekursiv vorgegangen, wie z . B. find.

Eine weitere gängige Lösung ist die Verwendung find -print0mit xargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

Beachten Sie, dass dies h:/gtatsächlich korrekt ist, da der Dateiname eine enthält \r.

Antwort4

Es ist ein bisschen schwierig, Ihre Leseschleife portabel zu machen, aber für Bash im Besonderen können Sie etwas wie versuchenDas.

Relevanter Teil:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

Dies weist an find, die Ausgabe durch NUL-Zeichen (0x00) getrennt zu drucken und durch readNUL getrennte Zeilen abzurufen ( -d $'\0'), ohne Backslashs als Escapezeichen für andere Zeichen zu verwenden ( -r), und keine Worttrennung in den Zeilen vorzunehmen ( IFS=). Da 0x00 ein Byte ist, das in Dateinamen oder Pfaden unter Unix nicht vorkommen kann, sollte dies alle Ihre seltsamen Dateinamenprobleme lösen.

verwandte Informationen