Quand va `trouver. -exec COMMAND {} + `exécuter COMMAND plusieurs fois?

8

Si je fais

find . -exec echo {} +

il imprime tous les chemins sur une seule ligne, c'est-à-dire que la commande echon'est exécutée qu'une seule fois.

Mais selon man find,

-exec command {} +
    ... the number of invocations of the command will 
be much  less  than  the  number  of matched files. ...

Il semble que dans certaines circonstances, la commande sera exécutée plusieurs fois. Ai-je raison? Veuillez illustrer.

flamme-gelée
la source

Réponses:

7

POSIX a défini find -exec nom_utilitaire [argument ...] {} + comme:

La fin de l'expression primaire doit être ponctuée d'un <semicolon> ou d'un <plus-sign>. Seul un <plus-sign> qui suit immédiatement un argument contenant uniquement les deux caractères "{}" doit ponctuer la fin de l'expression principale. Les autres utilisations de <plus-sign> ne doivent pas être traitées comme spéciales. Si l'expression principale est ponctuée d'un <semicolon>, l'utilitaire nom_utilitaire doit être invoqué une fois pour chaque chemin d'accès et le primaire doit être évalué comme vrai si l'utilitaire renvoie une valeur nulle comme état de sortie. Un nom_utilitaire ou un argument contenant uniquement les deux caractères "{}" doit être remplacé par le nom de chemin actuel. Si un nom_utilitaire ou un argumentla chaîne contient les deux caractères "{}", mais pas seulement les deux caractères "{}", elle est définie par l'implémentation, que find remplace ces deux caractères ou utilise la chaîne sans changement.

Si l'expression primaire est ponctuée d'un <plus-sign>, le primaire doit toujours être évalué comme vrai et les noms de chemins pour lesquels le primaire est évalué doivent être agrégés en ensembles. L'utilitaire nom_utilitaire doit être invoqué une fois pour chaque ensemble de noms de chemin agrégés. Chaque appel doit commencer après que le dernier nom de chemin dans l'ensemble est agrégé et doit être terminé avant que l' utilitaire de recherche ne se termine et avant que le premier nom de chemin dans l'ensemble suivant (le cas échéant) ne soit agrégé pour ce primaire, mais il n'est pas spécifié autrement si l'invocation se produit avant, pendant ou après les évaluations des autres primaires. Si une invocation renvoie une valeur non nulle comme état de sortie, la recherche l'utilitaire doit retourner un état de sortie non nul. Un argument ne contenant que les deux caractères "{}" doit être remplacé par l'ensemble de noms de chemins agrégés, chaque nom de chemin étant passé comme argument distinct à l'utilitaire appelé dans le même ordre qu'il a été agrégé. La taille de tout ensemble de deux ou plusieurs chemins d'accès doit être limitée de sorte que l'exécution de l'utilitaire ne provoque pas le dépassement de la limite {ARG_MAX} du système . Si plusieurs arguments contenant les deux caractères "{}" sont présents, le comportement n'est pas spécifié.

Lorsque la longueur du nom de fichier que vous avez trouvé dépasse le système ARG_MAX, la commande est exécutée.

Vous pouvez ARG_MAXutiliser getconf :

$ getconf ARG_MAX
2097152

Sur certains systèmes, la valeur réelle de ARG_MAXpeut être différente, vous pouvez vous référer ici pour plus de détails.

cuonglm
la source
J'ai effectué une expérience en utilisant find / -exec echo | wcet en mesurant le rapport entre le nombre de caractères et le nombre de lignes.J'ai trouvé que la longueur maximale de la ligne de commande utilisée par findétait considérablement plus petite que la limite POSIX théorique et beaucoup plus proche de la Size of command buffer we are actually usingligne dans la sortie de xargs --show-limits. Cela est vrai pour Linux et cela peut être vrai pour l'implémentation de Mac OS find, bien xargsqu'il n'imprime pas la valeur dans Mac OS. Une idée sur pourquoi cela se produit?
pqnet
--show-limitsn'est pas spécifié par POSIX, l'implémentation de Mac OS xargsne le prend pas en charge. find / -exec echo | wcne fonctionnera pas. N'oubliez pas que les ARG_MAXoctets de retour. Et c'est la longueur maximale des arguments des exec(3)fonctions.
cuonglm
Je sais que ce --show-limitsn'est pas POSIX, bien que ce ne soit pas la longueur d'argument maximale utilisée par find, qui utilise une valeur plus petite. Je ne comprends pas pourquoi vous dites que find / -exec echo | wccela ne fonctionnera pas: à mon avis, c'est un bon moyen d'avoir une estimation de la valeur réelle (et d'après ce que je peux voir, mieux que d'utiliser getconf ARG_MAX). De plus, mon système de fichiers est principalement sinon entièrement composé de caractères ASCII, donc le nombre de caractères est approximativement le même que le nombre d'octets.
pqnet
@pqnet: utilisez find / -exec sh -c 'echo $@ | wc -c' _ {} +isntead.
cuonglm
désolé de l'avoir mal écrit, j'ai utiliséfind / -exec echo {} + | wc -lc
pqnet
7

Il existe une longueur maximale de liste d'arguments pour un nouveau processus dans le système POSIX. finddivisera l'exécution si les chemins d'accès aux fichiers sont plus longs que cela. Pour voir la limite sur Linux, utilisez xargs --show-limits(ne fonctionne pas sous Mac OS, si quelqu'un connaît une meilleure alternative, veuillez commenter ici)

edit: volé directement de la réponse de Gnouc, la manière POSIX pour obtenir la longueur maximale de la liste d'arguments est getconf ARG_MAX. Cependant, j'ai exécuté une expérience sur ma machine mac os, et il semble que cela findutilise un peu plus de la moitié de ce nombre. Ceci est cohérent avec le fait que, sur le système où il fonctionne, xargs --show-limitsnous dit qu'il n'utilisera pas la longueur d'argument maximale (dans ce cas aussi, il utilisera environ la moitié de ce nombre), mais je n'ai pas trouvé d'explication pour ça.

edit 2: il semble que le seul moyen fiable de déterminer combien de paramètres findcolleront ensemble pour chaque invocation soit d'expérimenter, par exemple en exécutant

find / -exec echo {} + | wc -cl

Comme la sortie de findpossède une ligne pour chaque echoappel, il est possible de les compter en utilisant wc -l. Le nombre total d'octets echoed est la sortie de à la wc -cplace. En divisant l'un par l'autre, vous obtenez le nombre moyen d'octets dans les paramètres pour chaque appel de commande (bien qu'une valeur légèrement inférieure, en raison de l'arrondi, soit environ la moitié de la longueur moyenne d'un chemin dans votre système)

pqnet
la source
xargsn'utilise pas la longueur d'argument maximale complète car de nombreux programmes ajoutent quelques arguments supplémentaires, puis transmettent les arguments à d'autres programmes. Si xargsremplit les arguments au maximum absolu, de tels programmes se cassent, car il n'y aurait pas de place pour ces arguments supplémentaires.
hvd
@hvd est logique. Mais alors, existe-t-il un moyen POSIX de savoir quelle proportion du tampon est utilisée par xargsou find?
pqnet
Vous pouvez l'exécuter avec une très longue liste d'arguments, en déterminant le nombre d'arguments passés lors de la première invocation (quelque chose comme yes . | xargs | head -n 1 | wc -c) et en comparant cela à la sortie de getconf ARG_MAX. Mais, en fait, en essayant sur mon système, j'ai une différence si grande qu'il semble qu'il y ait plus que je ne le pense.
hvd
donc cela se résume à expérimenter ... Je mettrai à jour ma réponse
pqnet