Wie führe ich einen Befehl für jede Datei in einer tiefen Verzeichnisstruktur rekursiv aus?

7

Ich habe eine tiefe Verzeichnisstruktur mit einer großen Anzahl von Dateien (ca. 1 Million). Ich möchte einen Befehl gegen jeden ausführen. (In meinem Fall handelt es sich bei den Dateien um PNGs, für die ich optipng ausführen möchte.)

Ich habe das versucht:

find . -name *.png | xargs sudo optipng -o2

aber bekomme den Fehler argument list too long.

Ich gehe davon aus, dass xargs damit umgehen kann und dass es ein Problem mit meiner Syntax geben muss.

UpTheCreek
quelle
2
Achtung: Wenn Ihr aktuelle Verzeichnis PNG - Dateien enthält, wird die Schale erweitern , *.pngum ihre Namen - also doppelt , den Zweck des Befehls zu besiegen (nicht alle entsprechenden .pngDateien und möglicherweise verursacht eine argument list too longfür findsich selbst immer Zitat Dinge , die Sie weitergeben , wie sie ist.:find . -iname '*.png' | ...
Muru

Antworten:

7

Führen Sie die Ausführung direkt mit find und nicht über xargs aus.

sudo find . -name '*.png' -exec optipng -o {} +
Geraint Jones
quelle
Vielen Dank. Wird das die Option als sudo ausführen? Ich habe es sudo findmit xargs versucht und es nicht (ich musste das sudo neben xargs stellen) [übrigens nicht meine Gegenstimme]
UpTheCreek
2
Ja, das findwird als root ausgeführt und es erscheint, optipngwas, da es von einem Prozess erzeugt wird, der root gehört, auch als root ausgeführt wird.
Geraint Jones
1
Wissen Sie, ob es eine Möglichkeit gibt, n parallele Prozesse zu erzeugen? (zB mit xargs können Sie dies mit dem -P-Schalter tun)
UpTheCreek
1
Sie sollten zitieren *.png. Andernfalls werden PNG-Dateien, die sich im aktuellen Verzeichnis befinden, auf diese erweitert.
Barmar
4

xargsWie Sie sehen, funktioniert es nicht, sudo als Befehl zum Ausführen von zu verwenden . Versuchen Sie es anders herum:

find . -name '*.png' | sudo xargs optipng -o2

Der Unterschied besteht darin, dass Ihre Version einen Befehl wie erstellt

 sudo optipng -o2 file1 file2 file3 file4 ......

und sudo kann nicht mit so vielen Parametern umgehen.

Wenn Sie es umgekehrt machen, wird sudo einmal ausgeführt, was wiederum startet xargsund dann einen Befehl wie generiert

optipng -o2 file1 file2 file3 file4 ......

und wenn optipngviele Dateien als Parameter verarbeitet werden können, sollte es funktionieren.

Wenn das immer noch nicht funktioniert, müssen Sie -execstattdessen wie in Geraint's Antwort verwenden, aber dies sollte Ihr letzter Ausweg sein.

Sven
quelle
Übrigens - warum sollte -exec ein letzter Ausweg sein? (nicht vertraut, also nur neugierig)
UpTheCreek
2
Er sagt 1M Dateien, Bash ist der Typ, der argument list too longnicht sagt optipngoder sudo. Ihre einzige Option, wenn Sie wirklich 1 Million Dateien haben, ist die Verwendung-exec
Geraint Jones
4
@GeraintJones: Nein, denn es ist genau die Aufgabe xargs, dafür zu sorgen, dass dies nicht passiert.
Sven
6
Wenn der -execBefehl mit a +anstelle von a beendet ;wird, gruppiert find die Dateien in Gruppen, die klein genug sind, um den Fehler "Argumentliste zu lang" zu vermeiden. (Sie müssen auch ein {}Argument vor dem +angeben, das durch die Dateinamenliste ersetzt wird.) Diese Funktion war nicht im ursprünglichen Unix enthalten find, aber es gibt sie schon lange genug (in POSIX seit über 10 Jahren) ... es ist Zeit zu verlernen die Verwendung von xargs.
1
Sie müssen zitieren, *.pngfalls sich im aktuellen Verzeichnis übereinstimmende Dateien befinden.
Barmar
2

Noch ein Schuss darauf:

find . -name '*.png' | sudo xargs -n x -P y optipng -o2

Dadurch wird eine Option für xDateien und ysolche Prozesse parallel gestartet (dies kann Ihnen helfen, die Dinge schneller zu erledigen, wenn Sie einige Kerne übrig haben). Ein Wert von xmehr als 10 hat wahrscheinlich keinen großen Einfluss auf die Leistung.

Das wird helfen, die Argumentliste kurz zu halten. Sie könnten also wahrscheinlich dorthin gehen sudo, wo Sie es hatten, aber das ist nur ein weiterer Befehl, der für jeden Stapel ausgeführt werden müsste.

Fuchs
quelle