trouver -exec cmd {} + vs | xargs

115

Lequel est le plus efficace sur un très grand ensemble de fichiers et doit être utilisé?

find . -exec cmd {} +

ou

find . | xargs cmd

(Supposons qu'il n'y ait pas de personnages amusants dans les noms de fichiers)

dogbane
la source
En relation: stackoverflow.com/questions/9612090/…
Mateusz Piotrowski

Réponses:

107

La différence de vitesse sera insignifiante.

Mais vous devez vous assurer que:

  1. Votre script ne supposera pas qu'aucun fichier n'aura d'espace, de tabulation, etc. dans le nom de fichier; la première version est sûre, la seconde ne l'est pas.

  2. Votre script ne traitera pas un fichier commençant par " -" comme une option.

Donc, votre code devrait ressembler à ceci:

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

ou

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

La première version est plus courte et plus facile à écrire car vous pouvez ignorer 1, mais la deuxième version est plus portable et plus sûre, car " -exec cmd {} +" est une option relativement nouvelle dans GNU findutils (depuis 2005, de nombreux systèmes en cours d'exécution ne l'auront pas encore) et c'était buggy récemment . De plus, beaucoup de gens ne le savent pas " -exec cmd {} +", comme vous pouvez le voir dans d'autres réponses.

Tometzky
la source
4
-print0 est aussi une option de recherche GNU (et GNU xargs) qui manque sur de nombreux systèmes non Linux, donc l'argument de portabilité n'est pas aussi valide. Cependant, utiliser simplement -print et laisser le -0 hors de xargs est très portable.
dannysauer
7
Le fait est que sans -print0 cela ne fonctionne pas s'il y a un fichier avec un espace ou une tabulation etc. Cela peut être une faille de sécurité comme s'il y avait un nom de fichier comme "foo -o index.html" alors -o sera traité Comme une option. Essayez dans un répertoire vide: "touch - foo \ -o \ index.html; find. | Xargs cat". Vous obtiendrez: "cat: option invalide - 'o'"
Tometzky
2
Son exemple est un nom de fichier qui contient un -. Sans -print0, find crachera ./foo -o index.html. Alors peut-être que commencer par un - n'est pas un gros problème, mais le résultat a peu changé, et sur un système multi-utilisateurs, pourrait fournir un vecteur d'attaque si votre script est lisible par le monde entier.
bobpaul
2
Une note sur quelque chose qui m'a fait trébucher ici - l'utilisation execaffichera les résultats tels qu'ils sont trouvés, ce xargsqui, semble-t-il, attendra que tout le répertoire soit recherché avant d'écrire dans stdout. Si vous essayez ceci sur un grand répertoire et qu'il semble que cela xargsne fonctionne pas, la patience est recommandée.
FarmerGedden
1
@Motivated Without -print0find renvoie les noms de fichiers séparés par une nouvelle ligne, mais une nouvelle ligne peut également faire partie d'un nom de fichier, ce qui le rend ambigu. L'octet 0 ne peut pas, c'est donc un séparateur sûr. Oui - l'ajout --à une commande qui la prend en charge est une bonne pratique lorsque vous ne pouvez pas contrôler ses arguments, même si ce n'est pas toujours strictement requis ou dangereux.
Tometzky
7
find . | xargs cmd

est plus efficace (il s'exécute cmdle moins de fois possible, contrairement à execqui s'exécute cmdune fois pour chaque match). Cependant, vous rencontrerez des problèmes si les noms de fichiers contiennent des espaces ou des caractères géniaux.

Il est suggéré d'utiliser ce qui suit:

find . -print0 | xargs -0 cmd

cela fonctionnera même si les noms de fichiers contiennent des caractères géniaux ( -print0fait findimprimer des correspondances terminées par NUL, -0fait xargss'attendre à ce format.)

Demander
la source
28
Ce n'est pas "find. -Exec cmd {} \;" mais "trouver. -exec cmd {} +". Ce dernier n'exécutera pas un fichier à la fois.
Tometzky
2
Notez que l' xargsapproche est en fait beaucoup plus lente s'il n'y a pas (ou seulement quelques) fichiers correspondants et cmdn'a pas grand-chose à faire pour chaque fichier. Par exemple, lorsqu'elle est exécutée dans un répertoire vide, la xargsversion prendra au moins deux fois le temps, car deux processus doivent être démarrés au lieu d'un seul. (Oui, la différence est généralement imperceptible sur * nix, mais dans une boucle, cela peut être important; ou, essayez-le sous Windows quelque temps ...)
SamB
2

Les xargsversions modernes prennent souvent en charge l'exécution de pipeline parallèle.

Évidemment, cela pourrait être un point pivot lorsqu'il s'agit de choisir entre find … -exec et … | xargs

poige
la source