Diese Frage ist inspiriert von
Warum wird die Verwendung einer Shell-Schleife zum Verarbeiten von Text als schlechte Praxis angesehen?
Ich sehe diese Konstrukte
for file in `find . -type f -name ...`; do smth with ${file}; done
und
for dir in $(find . -type d -name ...); do smth with ${dir}; done
wird hier fast täglich verwendet, auch wenn einige Leute sich die Zeit nehmen, diese Posts zu kommentieren und zu erklären, warum diese Art von Zeug vermieden werden sollte…
Die Anzahl solcher Posts zu sehen (und die Tatsache, dass diese Kommentare manchmal einfach ignoriert) Ich dachte, ich könnte genauso gut eine Frage stellen:
Warum läuft eine Schleife über find
‘s Output Bad Practice und wie man einen oder mehrere Befehle für jeden Dateinamen/Pfad, der von find
zurückgegeben wird, richtig ausführt ?
Akzeptierte Antwort:
Das Problem
for f in $(find .)
kombiniert zwei unvereinbare Dinge.
find
druckt eine Liste von Dateipfaden, die durch Zeilenumbrüche getrennt sind. While der Split+Glob-Operator, der aufgerufen wird, wenn Sie $(find .)
verlassen In diesem Listenkontext ohne Anführungszeichen wird es auf die Zeichen von $IFS
aufgeteilt (enthält standardmäßig Zeilenumbruch, aber auch Leerzeichen und Tabulator (und NUL in zsh
)) und führt für jedes resultierende Wort ein Globbing durch (außer in zsh
). ) (und sogar Klammererweiterung in ksh93- oder pdksh-Derivaten!).
Auch wenn du es schaffst:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
Das ist immer noch falsch, da das Zeilenumbruchzeichen genauso gültig ist wie jedes andere in einem Dateipfad. Die Ausgabe von find -print
ist einfach nicht zuverlässig nachbearbeitbar (außer mit einem komplizierten Trick, wie hier gezeigt). ).
Das bedeutet auch, dass die Shell die Ausgabe von find
speichern muss vollständig und dann split+glob (was impliziert, dass diese Ausgabe ein zweites Mal im Speicher gespeichert wird), bevor Sie beginnen, die Dateien zu durchlaufen.
Beachten Sie, dass find . | xargs cmd
hat ähnliche Probleme (dort Leerzeichen, Zeilenvorschub, einfaches Anführungszeichen, doppeltes Anführungszeichen und umgekehrter Schrägstrich (und mit einigen xarg
Implementierungsbytes, die nicht Teil gültiger Zeichen sind) sind ein Problem)
Richtigere Alternativen
Die einzige Möglichkeit, einen for
zu verwenden Schleife auf der Ausgabe von find
wäre die Verwendung von zsh
das unterstützt IFS=$'