find -exec cmd {} + vs | xargs

115

Welches ist effizienter als ein sehr großer Satz von Dateien und sollte verwendet werden?

find . -exec cmd {} +

oder

find . | xargs cmd

(Angenommen, die Dateinamen enthalten keine lustigen Zeichen.)

Dogbane
quelle
Siehe auch
Mateusz Piotrowski

Antworten:

107

Der Geschwindigkeitsunterschied ist unbedeutend.

Aber Sie müssen sicherstellen, dass:

  1. Ihr Skript geht nicht davon aus, dass keine Datei Speicherplatz, Tabulator usw. im Dateinamen enthält. Die erste Version ist sicher, die zweite nicht.

  2. Ihr Skript behandelt eine Datei, die mit " -" beginnt, nicht als Option.

Ihr Code sollte also folgendermaßen aussehen:

find . -exec cmd -option1 -option2 -- {} +

oder

find . -print0 | xargs -0 cmd -option1 -option2 --

Die erste Version ist kürzer und einfacher zu schreiben, da Sie 1 ignorieren können, aber die zweite Version ist portabler und sicherer, da " -exec cmd {} +" eine relativ neue Option in GNU findutils ist (seit 2005 haben viele laufende Systeme sie noch nicht). und es war vor kurzem fehlerhaft . Auch viele Leute wissen das nicht " -exec cmd {} +", wie Sie aus anderen Antworten sehen können.

Tometzky
quelle
4
-print0 ist auch eine GNU-Suchoption (und GNU-xargs-Option), die in vielen Nicht-Linux-Systemen fehlt, sodass das Portabilitätsargument nicht so gültig ist. Es ist jedoch sehr portabel, nur -print zu verwenden und xargs -0 wegzulassen .
Dannysauer
7
Der Punkt ist, dass es ohne -print0 nicht funktioniert, wenn es eine Datei mit einem Leerzeichen oder einer Registerkarte usw. gibt. Dies kann eine Sicherheitslücke sein, als ob es einen Dateinamen wie "foo -o index.html" gibt, dann wird -o behandelt als eine Option. Versuchen Sie es in einem leeren Verzeichnis: "touch - foo \ -o \ index.html; find. | Xargs cat". Sie erhalten: "cat: ungültige Option - 'o'"
Tometzky
2
Sein Beispiel ist ein Dateiname, der ein - enthält. Ohne -print0 spuckt find ./foo -o index.html aus. Vielleicht ist es keine große Sache, mit einem - zu beginnen, aber das Ergebnis ändert sich kaum und könnte auf einem Mehrbenutzersystem einen Angriffsvektor liefern, wenn Ihr Skript weltweit lesbar ist.
Bobpaul
2
Ein Hinweis auf etwas, das mich hier oben gestolpert hat - mit execwird die Ergebnisse so ausgegeben, wie sie gefunden wurden xargs, wobei anscheinend gewartet wird, bis das gesamte Verzeichnis durchsucht ist, bevor in stdout geschrieben wird. Wenn Sie dies in einem großen Verzeichnis versuchen und es anscheinend xargsnicht funktioniert, ist Geduld ratsam.
FarmerGedden
1
@Motivated Without -print0find gibt Dateinamen zurück, die durch Zeilenumbrüche getrennt sind. Zeilenumbrüche können jedoch auch Teil eines Dateinamens sein, wodurch sie mehrdeutig werden. Byte 0 kann nicht, daher ist es ein sicheres Trennzeichen. Ja - das Hinzufügen --zu einem Befehl, der ihn unterstützt, ist eine gute Vorgehensweise, wenn Sie seine Argumente nicht kontrollieren können, auch wenn dies nicht immer unbedingt erforderlich oder unsicher ist.
Tometzky
7
find . | xargs cmd

ist effizienter (es läuft cmdso wenig wie möglich, im Gegensatz zu exec, die ausgeführt wird cmdfür jedes Spiel einmal). Sie werden jedoch auf Probleme stoßen, wenn Dateinamen Leerzeichen oder funky Zeichen enthalten.

Es wird empfohlen, Folgendes zu verwenden:

find . -print0 | xargs -0 cmd

Dies funktioniert sogar , wenn die Dateinamen enthalten flippige Zeichen ( -print0Marken findNUL-terminierten Matches drucken, -0macht xargserwarten dieses Format.)

Fragen
quelle
28
Dies ist nicht "find. -Exec cmd {} \;" aber "find. -exec cmd {} +". Letzteres führt nicht jeweils eine Datei aus.
Tometzky
2
Beachten Sie, dass der xargsAnsatz tatsächlich erheblich langsamer ist, wenn keine (oder nur wenige) übereinstimmende Dateien vorhanden sind und cmdnicht für jede Datei viel zu tun ist. Wenn die xargsVersion beispielsweise in einem leeren Verzeichnis ausgeführt wird, dauert sie mindestens doppelt so lange, da zwei Prozesse anstelle von nur einem gestartet werden müssen. (Ja, der Unterschied ist normalerweise auf * nix nicht wahrnehmbar, aber in einer Schleife könnte es wichtig sein; oder versuchen Sie es einige Zeit unter Windows ...)
SamB
2

Moderne xargsVersionen unterstützen häufig die parallele Pipeline-Ausführung.

Offensichtlich könnte es ein Dreh- und Angelpunkt sein, wenn es um die Wahl zwischen find … -exec und geht … | xargs

poige
quelle