Diese Frage ist inspiriert von
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
wobei hier fast auf einer täglichen Basis verwendet , auch wenn einige Leute die Zeit nehmen , auf diesen Beiträgen kommentieren zu erklären , warum diese Art von Sachen vermieden werden sollte ...
Sehen die Anzahl solcher Stellen (und die Tatsache , dass manchmal diese Kommentare werden einfach ignoriert) Ich dachte, ich könnte genauso gut eine Frage stellen:
Warum ist das Schleifen find
der Ausgabe eine schlechte Übung und wie können Sie einen oder mehrere Befehle für jeden Dateinamen / Pfad ausführen, der von zurückgegeben wird find
?
Antworten:
Das Problem
kombiniert zwei inkompatible Dinge.
find
Gibt eine Liste der Dateipfade aus, die durch Zeilenumbrüche begrenzt sind. Während der split + glob-Operator, der aufgerufen wird, wenn Sie diesen$(find .)
in diesem$IFS
Listenkontext nicht zitierten Operator verwenden, ihn in die Zeichen von (standardmäßig Newline, aber auch Leerzeichen und Tabulatorzeichen (und NUL inzsh
)) aufteilt und mit jedem resultierenden Wort (mit Ausnahme von) ein Globen ausführt inzsh
) (und sogar die Erweiterung in ksh93- oder pdksh-Derivaten abgleichen!).Auch wenn du es schaffst:
Das ist immer noch falsch, da das Newline-Zeichen 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 verschlungenen Trick, wie hier gezeigt ).Das bedeutet auch, dass die Shell die Ausgabe von
find
vollständig speichern und dann + glob aufteilen muss (was impliziert, dass diese Ausgabe ein zweites Mal im Speicher gespeichert wird), bevor eine Schleife über die Dateien gestartet wird.Beachten Sie, dass
find . | xargs cmd
ähnliche Probleme auftreten (Leerzeichen, Zeilenumbrüche, einfache Anführungszeichen, doppelte Anführungszeichen und umgekehrte Schrägstriche (und bei einigenxarg
Implementierungen sind Bytes, die nicht Teil gültiger Zeichen sind), ein Problem.)Richtigere Alternativen
Die einzige Möglichkeit, eine
for
Schleife für die Ausgabe vonfind
zu verwendenzsh
, ist die Verwendung vonIFS=$'\0'
und:(Ersetzen
-print0
durch-exec printf '%s\0' {} +
fürfind
Implementierungen, die nicht den Standard unterstützen (aber heutzutage durchaus üblich)-print0
).Hier ist der richtige und tragbare Weg zu verwenden
-exec
:Oder wenn
something
Sie mehr als ein Argument annehmen können:Wenn Sie diese Liste von Dateien benötigen, die von einer Shell verarbeitet werden sollen:
(Vorsicht, es können mehrere gestartet werden
sh
).Auf einigen Systemen können Sie Folgendes verwenden:
aber , dass wenig Vorteil gegenüber der Standard - Syntax und Mittel
something
sindstdin
entweder das Rohr oder die/dev/null
.Ein Grund dafür könnte sein, dass Sie die
-P
Option GNUxargs
für die parallele Verarbeitung verwenden. Dasstdin
Problem kann auch mit GNU umgangen werden,xargs
mit der-a
Option, dass Shells die Prozessersetzung unterstützen:Zum Beispiel, um bis zu 4 gleichzeitige Aufrufe von
something
jeweils 20 Dateiargumenten auszuführen .Mit
zsh
oderbash
können Sie die Ausgabe von auffind -print0
folgende Weise durchlaufen :read -d ''
Liest NUL-getrennte Datensätze anstelle von Zeilenumbrüchen.bash-4.4
und darüber können auch Dateien gespeichert werden, die vonfind -print0
in einem Array zurückgegeben wurden mit:Das
zsh
Äquivalent (das den Vorteil hat, denfind
Ausgangsstatus beizubehalten):Mit
zsh
können Sie die meistenfind
Ausdrücke in eine Kombination aus rekursivem Globbing und Glob-Qualifikationsmerkmalen übersetzen. Eine Schleifefind . -name '*.txt' -type f -mtime -1
wäre zum Beispiel:Oder
(Vorsicht : die Notwendigkeit ,
--
wie bei**/*
, Dateipfade beginnen , nicht./
, so kann mit beginnen-
zum Beispiel).ksh93
undbash
schließlich hinzugefügt Unterstützung für**/
(wenn auch nicht mehr fortgeschrittene Formen des rekursiven Globbing), aber immer noch nicht die Glob-Qualifikatoren, die die Verwendung von dort**
sehr begrenzt macht. Beachten Sie auch, dassbash
vor 4.3 beim Abstieg in den Verzeichnisbaum Symlinks folgen.Wie beim Loop-Over bedeutet dies auch
$(find .)
, dass die gesamte Liste der Dateien in Speicher 1 abgelegt wird . Dies kann jedoch in einigen Fällen wünschenswert sein, wenn Sie nicht möchten, dass Ihre Aktionen für die Dateien einen Einfluss auf die Suche nach Dateien haben (z. B. wenn Sie weitere Dateien hinzufügen, die möglicherweise selbst gefunden werden).Sonstige Überlegungen zur Zuverlässigkeit / Sicherheit
Rennbedingungen
Wenn wir jetzt von Zuverlässigkeit sprechen, müssen wir die Rennbedingungen zwischen dem Zeitpunkt
find
/ dem Auffindenzsh
einer Datei erwähnen und prüfen , ob sie den Kriterien und dem Zeitpunkt, zu dem sie verwendet wird, entspricht ( TOCTOU-Rennen ).Selbst wenn man einen Verzeichnisbaum herunterfährt, muss man darauf achten, dass man Symlinks nicht folgt und das ohne TOCTOU-Rennen.
find
(GNUfind
zumindest) tut dem durch die Verzeichnisse Öffnen mitopenat()
mit den richtigenO_NOFOLLOW
Flags (sofern unterstützt) und eine Dateibeschreibung für jedes Verzeichnis offen zu halten,zsh
/bash
/ksh
tu das nicht. Wenn ein Angreifer also in der Lage ist, ein Verzeichnis zum richtigen Zeitpunkt durch einen Symlink zu ersetzen, kann dies dazu führen, dass das falsche Verzeichnis gefunden wird.Selbst wenn
find
das Verzeichnis ordnungsgemäß heruntergefahren wird, mit-exec cmd {} \;
und noch mehr mit-exec cmd {} +
, wenncmd
es einmal ausgeführt wird, zum Beispiel wenncmd ./foo/bar
odercmd ./foo/bar ./foo/bar/baz
wenn die Zeit davoncmd
Gebrauch macht./foo/bar
,bar
erfüllen die Attribute von möglicherweise nicht mehr die Kriterien, die mit übereinstimmenfind
, aber noch schlimmer./foo
sind ersetzt durch einen Symlink zu einem anderen Ort (und das Rennfenster ist viel größer,-exec {} +
da darauffind
gewartet wird, dass genügend Dateien zum Aufrufen vorhanden sindcmd
).Einige
find
Implementierungen haben ein (noch nicht standardmäßiges)-execdir
Prädikat, um das zweite Problem zu lösen.Mit:
find
chdir()
s in das übergeordnete Verzeichnis der Datei, bevor Sie sie ausführencmd
. Anstatt aufzurufencmd -- ./foo/bar
, ruft escmd -- ./bar
(cmd -- bar
bei einigen Implementierungen, daher das--
) auf, sodass das Problem./foo
vermieden wird, in einen Symlink geändert zu werden. Das macht die Verwendung von Befehlenrm
sicherer (es könnte immer noch eine andere Datei entfernen, aber keine Datei in einem anderen Verzeichnis), aber keine Befehle, die die Dateien möglicherweise ändern, es sei denn, sie wurden so konzipiert, dass sie Symlinks nicht folgen.-execdir cmd -- {} +
manchmal funktioniert es auch, aber mit mehreren Implementierungen, einschließlich einiger Versionen von GNUfind
, ist es äquivalent zu-execdir cmd -- {} \;
.-execdir
hat auch den Vorteil, einige der Probleme zu umgehen, die mit zu tiefen Verzeichnisbäumen verbunden sind.Im:
Die Größe des angegebenen Pfads
cmd
nimmt mit der Tiefe des Verzeichnisses zu, in dem sich die Datei befindet. Wenn diese Größe größer wird alsPATH_MAX
(etwa 4 KB unter Linux),cmd
schlägt jeder Systemaufruf fehl , der auf diesem Pfad ausgeführt wirdENAMETOOLONG
.Mit
-execdir
wird nur der Dateiname (ggf. vorangestellt./
) übergebencmd
. Die Dateinamen selbst haben auf den meisten Dateisystemen eine viel niedrigere Grenze (NAME_MAX
) alsPATH_MAX
, sodass derENAMETOOLONG
Fehler mit geringerer Wahrscheinlichkeit auftritt.Bytes vs Zeichen
Außerdem wird bei der Betrachtung der Sicherheit
find
und allgemeiner beim Umgang mit Dateinamen im Allgemeinen häufig die Tatsache übersehen , dass Dateinamen auf den meisten Unix-ähnlichen Systemen Folgen von Bytes sind (jeder Byte-Wert außer 0 in einem Dateipfad und auf den meisten Systemen). ASCII-basierte, wir werden die seltenen EBCDIC-basierten vorerst ignorieren. 0x2f ist der Pfadbegrenzer.Es liegt an den Anwendungen, zu entscheiden, ob sie diese Bytes als Text betrachten möchten. Und das tun sie im Allgemeinen, aber im Allgemeinen erfolgt die Übersetzung von Bytes in Zeichen basierend auf dem Gebietsschema des Benutzers, basierend auf der Umgebung.
Dies bedeutet, dass ein gegebener Dateiname je nach Gebietsschema unterschiedliche Textdarstellungen haben kann. Die Bytesequenz
63 f4 74 e9 2e 74 78 74
würde beispielsweisecôté.txt
für eine Anwendung gelten, die diesen Dateinamen in einem Gebietsschema interpretiert, in dem der Zeichensatz ISO-8859-1 lautet, undcєtщ.txt
in einem Gebietsschema, in dem der Zeichensatz stattdessen IS0-8859-5 lautet.Schlechter. In einem Gebietsschema, in dem der Zeichensatz UTF-8 ist (die heutige Norm), konnten 63 f4 74 e9 2e 74 78 74 einfach keinen Zeichen zugeordnet werden!
find
ist eine solche Anwendung, die Dateinamen als Text für ihre-name
/-path
Prädikate betrachtet (und mehr, wie-iname
oder-regex
mit einigen Implementierungen).Was das bedeutet, ist das zum Beispiel mit mehreren
find
Implementierungen (einschließlich GNUfind
).würde unsere
63 f4 74 e9 2e 74 78 74
obige Datei nicht finden, wenn sie in einem UTF-8-Gebietsschema aufgerufen wird, da*
(das mit 0 oder mehr Zeichen übereinstimmt, nicht mit Bytes) nicht mit diesen Nicht-Zeichen übereinstimmen könnte.LC_ALL=C find...
würde das Problem umgehen, da das Gebietsschema C ein Byte pro Zeichen impliziert und (im Allgemeinen) garantiert, dass alle Bytewerte einem Zeichen zugeordnet sind (obwohl möglicherweise undefinierte für einige Bytewerte).Wenn es nun darum geht, diese Dateinamen von einer Shell zu durchlaufen, kann dieses Byte gegen das Zeichen ebenfalls ein Problem werden. Wir sehen in der Regel vier Haupttypen von Muscheln in dieser Hinsicht:
Diejenigen, die noch nicht Multibyte-fähig sind, mögen
dash
. Für sie ist ein Byte einem Zeichen zugeordnet. In UTF-8 sind dascôté
beispielsweise 4 Zeichen, aber 6 Bytes. In einem Gebietsschema, in dem UTF-8 der Zeichensatz ist, infind
findet erfolgreich die Dateien, deren Name aus 4 in UTF-8 codierten Zeichen besteht, gibt jedochdash
Längen zwischen 4 und 24 aus.yash
: das Gegenteil. Es geht nur um Charaktere . Alle Eingaben werden intern in Zeichen übersetzt. Dies sorgt für die konsistenteste Shell, bedeutet aber auch, dass keine willkürlichen Byte-Sequenzen verarbeitet werden können (solche, die sich nicht in gültige Zeichen übersetzen lassen). Selbst im Gebietsschema C können keine Bytewerte über 0x7f verarbeitet werden.in einem UTF-8-Gebietsschema schlägt beispielsweise auf unserer ISO-8859-1
côté.txt
von früher fehl .Solche wie
bash
oderzsh
wo die Multi-Byte-Unterstützung nach und nach hinzugefügt wurde. Diese werden auf die Berücksichtigung von Bytes zurückgreifen, die nicht wie Zeichen auf Zeichen abgebildet werden können. Hier und da gibt es immer noch ein paar Fehler, insbesondere bei weniger gebräuchlichen Multi-Byte-Zeichensätzen wie GBK oder BIG5-HKSCS (die ziemlich unangenehm sind, da viele ihrer Multi-Byte-Zeichen Bytes im Bereich 0-127 enthalten (wie die ASCII-Zeichen). ).Diejenigen wie die
sh
von FreeBSD (mindestens 11) odermksh -o utf8-mode
die Multi-Bytes unterstützen, aber nur für UTF-8.Anmerkungen
1 Der Vollständigkeit halber können wir einen Hacky-In-Weg erwähnen
zsh
, um Dateien mithilfe von rekursivem Globbing zu durchlaufen, ohne die gesamte Liste im Speicher zu speichern:+cmd
ist ein Glob-Qualifizierer, dercmd
(normalerweise eine Funktion) mit dem aktuellen Dateipfad in aufruft$REPLY
. Die Funktion gibt true oder false zurück, um zu entscheiden, ob die Datei ausgewählt werden soll (und kann auch$REPLY
mehrere Dateien in einem$reply
Array ändern oder zurückgeben ). Hier führen wir die Verarbeitung in dieser Funktion durch und geben false zurück, damit die Datei nicht ausgewählt wird.quelle
find
Verhalten zu verwandeln . Globbing ist standardmäßig sicher, während find standardmäßig unsicher ist.Die einfache Antwort lautet:
Weil Dateinamen beliebige Zeichen enthalten können .
Daher gibt es kein druckbares Zeichen, mit dem Sie Dateinamen zuverlässig abgrenzen können.
Zeilenumbrüche werden häufig (fälschlicherweise) zum Abgrenzen von Dateinamen verwendet, da es ungewöhnlich ist , Zeilenumbrüche in Dateinamen aufzunehmen.
Wenn Sie Ihre Software jedoch auf willkürlichen Annahmen aufbauen, können Sie im besten Fall nicht mit ungewöhnlichen Fällen umgehen und sind im schlimmsten Fall böswilligen Exploits ausgesetzt, die die Kontrolle über Ihr System verlieren. Es geht also um Robustheit und Sicherheit.
Wenn Sie Software auf zwei verschiedene Arten schreiben können und eine von ihnen Randfälle (ungewöhnliche Eingaben) korrekt behandelt, die andere jedoch besser lesbar ist, könnten Sie argumentieren, dass ein Kompromiss besteht. (Ich würde nicht. Ich bevorzuge richtigen Code.)
Wenn jedoch die richtige, robuste Version des Codes auch leicht zu lesen ist, gibt es keine Entschuldigung für das Schreiben von Code, der in Randfällen fehlschlägt. Dies ist der Fall
find
und die Notwendigkeit, einen Befehl für jede gefundene Datei auszuführen.Lassen Sie uns genauer sein: Auf einem UNIX- oder Linux-System können Dateinamen mit Ausnahme von a
/
(das als Pfadkomponententrennzeichen verwendet wird) beliebige Zeichen enthalten und dürfen kein Null-Byte enthalten.Ein Null-Byte ist daher der einzig richtige Weg, um Dateinamen zu begrenzen.
Da GNU
find
eine-print0
Primärdatei enthält, die ein Null-Byte zur Begrenzung der ausgegebenen Dateinamen verwendet,find
kann GNU problemlos mit GNUxargs
und seinem-0
Flag (und-r
Flag) verwendet werden, um die Ausgabe vonfind
:Es gibt jedoch keinen guten Grund , dieses Formular zu verwenden, weil:
find
ist so konzipiert , dass Befehle für die gefundenen Dateien ausgeführt werden können.Außerdem
xargs
benötigt GNU-0
und-r
, während FreeBSDxargs
nur benötigt-0
(und keine-r
Option hat) und einigexargs
überhaupt nicht unterstützen-0
. Halten Sie sich also am besten an die POSIX-Funktionen vonfind
(siehe nächster Abschnitt) und überspringen Sie diesexargs
.Was die
find
Fähigkeit von Punkt 2 betrifft, Befehle für die gefundenen Dateien auszuführen, so hat Mike Loukides das Beste gesagt:POSIX spezifizierte Verwendungen von
find
Um einen einzelnen Befehl für jede gefundene Datei auszuführen, verwenden Sie:
Um mehrere Befehle nacheinander für jede gefundene Datei auszuführen, wobei der zweite Befehl nur ausgeführt werden sollte, wenn der erste Befehl erfolgreich ist, verwenden Sie:
So führen Sie einen einzelnen Befehl für mehrere Dateien gleichzeitig aus:
find
in Kombination mitsh
Wenn Sie im Befehl Shell- Funktionen verwenden müssen, z. B. die Ausgabe umleiten oder eine Erweiterung vom Dateinamen oder Ähnlichem entfernen möchten, können Sie das
sh -c
Konstrukt verwenden. Sie sollten ein paar Dinge darüber wissen:Niemals
{}
direkt in densh
Code einbetten . Dies ermöglicht die Ausführung von willkürlichem Code aus in böswilliger Absicht erstellten Dateinamen. Außerdem wird von POSIX nicht einmal spezifiziert, dass es überhaupt funktioniert. (Siehe nächster Punkt.)Verwenden Sie es nicht
{}
mehrmals oder als Teil eines längeren Arguments. Dies ist nicht portabel. Tun Sie dies zum Beispiel nicht:find ... -exec cp {} somedir/{}.bak \;
So zitieren Sie die POSIX-Spezifikationen für
find
:Die Argumente, die auf die an die
-c
Option übergebene Shell-Befehlszeichenfolge folgen , werden beginnend mit$0
auf die Positionsparameter der Shell gesetzt . Nicht anfangen mit$1
.Aus diesem Grund empfiehlt es sich, einen "Dummy"
$0
-Wert einzufügenfind-sh
, der beispielsweise für die Fehlerberichterstattung in der erstellten Shell verwendet wird. Dies ermöglicht auch die Verwendung von Konstrukten, z. B."$@"
wenn mehrere Dateien an die Shell übergeben werden. Wenn Sie jedoch einen Wert für weglassen$0
, wird die zuerst übergebene Datei auf gesetzt$0
und somit nicht in die Shell aufgenommen"$@"
.Verwenden Sie zum Ausführen eines einzelnen Shell-Befehls pro Datei Folgendes:
Es ist jedoch in der Regel besser, die Dateien in einer Shell-Schleife zu verarbeiten, damit Sie nicht für jede einzelne gefundene Datei eine Shell erzeugen:
(Beachten Sie, dass
for f do
diesfor f in "$@"; do
den einzelnen Positionsparametern entspricht und mit ihnen umgeht. Mit anderen Worten, es werden alle Dateien verwendet, die von gefunden wurdenfind
, unabhängig von Sonderzeichen in ihren Namen.)Weitere Beispiele für die korrekte
find
Verwendung:(Hinweis: Sie können diese Liste jederzeit erweitern.)
quelle
find
-Ausgabe kenne - wo Sie Befehle in der aktuellen Shell ausführen müssen (z. B. weil Sie Variablen festlegen möchten) für jede Datei. In diesem Fallwhile IFS= read -r -u3 -d '' file; do ... done 3< <(find ... -print0)
ist das die beste Sprache, die ich kenne. Anmerkungen:<( )
ist nicht portabel - verwende bash oder zsh. Auch das-u3
und3<
gibt es für den Fall, dass irgendetwas in der Schleife versucht, stdin zu lesen.find ... -exec
Anrufs behandelt werden sollte. Oder verwenden Sie einfach ein Shell-Glob, wenn es Ihren Anwendungsfall behandelt.filelist=(); while ... do filelist+=("$file"); done ...
).find
Ausgabe zu schreiben oder sogar schlechter zu verwendenls
. Ich mache das täglich ohne Probleme. Ich kenne die Optionen -print0, --null, -z oder -0 für alle Arten von Werkzeugen. Aber ich würde keine Zeit damit verschwenden, sie in meiner interaktiven Shell-Eingabeaufforderung zu verwenden, es sei denn, dies wird wirklich benötigt. Dies könnte auch in Ihrer Antwort vermerkt sein.Diese Antwort bezieht sich auf sehr große Ergebnismengen und betrifft hauptsächlich die Leistung, z. B. beim Abrufen einer Liste von Dateien über ein langsames Netzwerk. Für kleine Mengen von Dateien (sagen wir einige 100 oder vielleicht sogar 1000 auf einer lokalen Festplatte) ist das meiste umstritten.
Parallelität und Speichernutzung
Abgesehen von den anderen gegebenen Antworten, die sich auf Trennungsprobleme und dergleichen beziehen, gibt es ein weiteres Problem mit
Der Teil innerhalb der Backticks muss zuerst vollständig ausgewertet werden, bevor er auf die Zeilenumbrüche aufgeteilt wird. Wenn Sie also eine große Anzahl von Dateien erhalten, kann dies dazu führen, dass die Größe der verschiedenen Komponenten begrenzt wird. Wenn es keine Grenzen gibt, ist möglicherweise kein Speicher mehr verfügbar. und in jedem Fall müssen Sie warten, bis die gesamte Liste von ausgegeben
find
und dann von analysiert wurde,for
bevor Sie überhaupt Ihre erste ausführensmth
.Die bevorzugte Unix-Methode besteht darin, mit parallel laufenden Pipes zu arbeiten, die im Allgemeinen auch keine willkürlich großen Puffer benötigen. Das heißt: Sie würden es vorziehen, wenn das Programm
find
parallel zu Ihrem ausgeführt wirdsmth
, und den aktuellen Dateinamen nur im RAM belassen, während das Programm dies übergibtsmth
.Eine zumindest teilweise okische Lösung dafür ist die vorgenannte
find -exec smth
. Sie müssen nicht mehr alle Dateinamen im Speicher behalten und werden problemlos parallel ausgeführt. Leider startet es auch einensmth
Prozess pro Datei. Wennsmth
nur eine Datei bearbeitet werden kann, muss das so sein.Die optimale Lösung wäre
find -print0 | smth
, wenn überhaupt möglich,smth
Dateinamen auf der STDIN zu verarbeiten. Dann haben Sie nur einensmth
Prozess, egal wie viele Dateien es gibt, und Sie müssen nur eine kleine Menge von Bytes (unabhängig von der Pipe-Pufferung) zwischen den beiden Prozessen puffern. Dies ist natürlich ziemlich unrealistisch, wennsmth
es sich um einen Standard-Unix / POSIX-Befehl handelt, kann aber ein Ansatz sein, wenn Sie ihn selbst schreiben.Wenn dies nicht möglich
find -print0 | xargs -0 smth
ist, ist dies wahrscheinlich eine der besseren Lösungen. Wie in den Kommentaren unter @ dave_thompson_085 erwähnt,xargs
werden die Argumente auf mehrere Durchläufe aufgeteilt,smth
wenn Systemgrenzen erreicht werden (standardmäßig im Bereich von 128 KB oder in einem vom System vorgegebenen Bereichexec
), und es stehen Optionen zur Verfügung , um die Anzahl zu beeinflussen Dateien werden an einen Aufruf von übergebensmth
, wodurch ein Gleichgewicht zwischen Anzahl dersmth
Prozesse und anfänglicher Verzögerung gefunden wird.BEARBEITEN: die Begriffe "am besten" entfernt - es ist schwer zu sagen, ob etwas Besseres auftaucht. ;)
quelle
find ... -exec smth {} +
ist die Lösung.find -print0 | xargs smth
funktioniert überhaupt nicht, aberfind -print0 | xargs -0 smth
(Anmerkung-0
) oderfind | xargs smth
wenn Dateinamen keine Anführungszeichen oder Backslashs enthalten, wird einersmth
mit so vielen verfügbaren Dateinamen ausgeführt, die in eine Argumentliste passen . Wenn Sie maxargs überschreiten, wird essmth
so oft ausgeführt, wie erforderlich, um alle angegebenen Argumente zu verarbeiten (keine Begrenzung). Mit können Sie kleinere 'Chunks' (also etwas frühere Parallelität) setzen-L/--max-lines -n/--max-args -s/--max-chars
.Ein Grund dafür ist, dass Whitespace in den Werken einen Schraubenschlüssel wirft und bewirkt, dass die Datei 'foo bar' als 'foo' und 'bar' ausgewertet wird.
Funktioniert in Ordnung, wenn stattdessen -exec verwendet wird
quelle
find
es eine Option gibt, mit der ein Befehl für jede Datei ausgeführt werden kann, ist dies ohne Weiteres die beste Option.-exec ... {} \;
versus-exec ... {} +
for file in "$(find . -type f)"
undecho "${file}"
dann funktioniert es auch mit Leerzeichen, andere Sonderzeichen, die ich denke, mehr Ärger verursachenfor file in "$(find . -type f)";do printf '%s %s\n' name: "${file}";done
Sie in einem Verzeichnis mit mehreren Dateien, die (Ihrer Meinung nach) jeden Dateinamen in einer separaten Zeile mit vorangestelltem Text ausgeben sollenname:
. Das tut es nicht.Da die Ausgabe eines Befehls eine einzelne Zeichenfolge ist, für die Schleife jedoch ein Array von Zeichenfolgen erforderlich ist. Der Grund, warum es "funktioniert", ist, dass Muscheln die Zeichenfolge in Leerzeichen für Sie aufteilen.
Zweitens, es sei denn, Sie benötigen ein bestimmtes Feature von
find
, müssen Sie sich darüber im Klaren sein, dass Ihre Shell höchstwahrscheinlich bereits ein rekursives Glob-Muster von selbst erweitern kann und dass es sich zu einem richtigen Array erweitern lässt.Bash-Beispiel:
Gleiche in Fisch:
Wenn Sie die Funktionen von benötigen
find
, stellen Sie sicher, dass Sie nur auf NUL aufteilen (z. B. diefind -print0 | xargs -r0
Redewendung).Fische können NUL-begrenzte Ausgaben durchlaufen. Also das ist eigentlich nicht schlecht:
Als letzte wenig gotcha, in vielen Muscheln (nicht Fisch natürlich), werden Schleifen über Befehlsausgabe der Schleife machen eine Subshell (dh Sie keine Variable in irgendeiner Weise einstellen kann , die sichtbar ist , nachdem die Schleife endet), die Niemals was du willst.
quelle
zsh
in den frühen 90er Jahren begann (obwohl Sie es dort benötigen würden**/*
).fish
Wie bei früheren Implementierungen von bashs äquivalentem Feature werden beim Abstieg in den Verzeichnisbaum jedoch Symlinks verwendet. Die Unterschiede zwischen den Implementierungen finden Sie unter Das Ergebnis von ls *, ls ** und ls *** .Das Durchlaufen der Ausgabe von find ist keine schlechte Übung. In dieser und allen anderen Situationen wird davon ausgegangen, dass Ihre Eingabe ein bestimmtes Format hat, anstatt zu wissen (zu testen und zu bestätigen), dass es ein bestimmtes Format ist.
tldr / cbf:
find | parallel stuff
quelle