combien d'appels de commandes avec la commande find -exec {} +
trouver les états de page de manuel:
-exec command {} +
This variant of the -exec action runs the specified command on the selected files,
but the command line is built by appending each selected file name at the end;
the total number of invocations of the command will be
much less than the number of matched files.
J'ai toujours pensé que cela provoquerait findune exécution commandexactement une fois. Existe-t-il un moyen de savoir combien de fois la commande est appelée?
Notez que c'est important car si ce n'est qu'une seule fois comme je le pensais, alors il y a danger de construire une liste d'arguments trop volumineuse pour commandêtre manipulée; mais si find finit par diviser les invocations (un peu similaire à parallel), alors cela serait atténué.
Réponses
Le tampon utilisé dépend de la findversion et semble avoir une taille d'environ 256 Ko dans la boîte SuSE que j'ai disponible ici.
Donc, pour calculer combien de fois la "commande" est appelée, vous devez connaître la longueur de chaque chemin de fichier trouvé, puis ce serait (approximativement) la somme de toutes les longueurs de chemin augmentées de un pour l'espace de division, moins la commande lui-même, divisé par la taille du tampon.
Par exemple, vous trouvez 20 000 fichiers avec une longueur de chemin moyenne de 200 octets, soit 4 020 000 octets, divisés par 256 Ko soit 15,33, donc vous auriez besoin d'environ 16 appels.
Le calcul exact serait légèrement plus complexe pour prendre en compte la nécessité de ne pas casser un chemin de fichier entre deux appels consécutifs, mais vous obtenez un chiffre approximatif.
Vous voyez ici un fil (avec le code source) où la taille est signalée comme 32Kb, et jugés inutilement bas (maintenant que je pense, peut - être moi - même find est en utilisant les syslimits je ne l' ai pas expérimenté.); coreutilsLa version de, par déduction, semble être quatre fois supérieure, soit 128 Kb .
La limite dépendra find(1)des tampons de s et de ce que la commande gère (en fonction du noyau). À moins que le tout dernier pourcentage de performances ne soit critique, les valeurs par défaut de votre système devraient être correctes.
Si vous vous souciez des performances, considérez l' ensemble du système qui le fait et mesurez où se trouvent les goulots d'étranglement. Il y a de fortes chances que vous soyez très surpris par vos découvertes. Bentley, dans ses délicieux "Ecrire des programmes efficaces" (Prentice-Hall, 1982), malheureusement épuisés depuis longtemps, partage plusieurs histoires d '"optimisations" prudentes qui ont rendu le code essentiellement inutilisé et fatalement bogué "plus rapide" ou optimisé la boucle inactive système d'exploitation après avoir mesuré qu'il prenait une fraction substantielle du temps de l'ordinateur. Les gens sont notoirement mauvais pour deviner où se situent les inefficacités. En outre, travailler aux niveaux supérieurs (architecture du système, organisation globale, algorithmes et structures de données) est bien plus rentable que sur les détails.
Note préliminaire: le manuel et votre question utilisent commandpour désigner la commande, mais puisque POSIX définit un utilitaire littéralement nommé command, ma réponse l'utilisera cmmnd.
Si vous voulez réellement exécuter cmmnd(s) et simplement compter le nombre d'appels (pour le savoir après la find fin), créez un wrapper qui fait quelque chose que vous pouvez compter (par exemple, imprime vers stderr, imprime dans un fichier journal, émet des bips) et exécute finalement le cmmnd. Exemple:
#!/bin/sh
echo "invoking cmmnd" >&2
cmmnd "$@"
Utilisez ensuite le wrapperau lieu de l' cmmndintérieur find.
Remarque findutilisera le /absolute/path/to/wrapperlors de la création de commandes qui ne sont pas trop longues; alors le wrapper utilisera /absolute/path/to/cmmnd. Si ce dernier est plus long, alors certaines lignes de commande qui le contiennent peuvent s'avérer trop longues de toute façon. Cette approche n'est donc pas aussi simple que nous le souhaitons. Vous pouvez étendre l'ancien chemin en le fournissant findtextuellement avec des barres obliques supplémentaires (par exemple /absolute/path/to/////wrapper).
Maintenant, je suppose que vous voulez connaître le nombre avant de décider de courir cmmnd(s). Comme dans un cas où appeler le cmmnddeux fois est une mauvaise chose (pour une raison quelconque) et vous voulez vous assurer findqu'il l'exécutera exactement une fois.
Le wrapper ci-dessus avec cmmnd "$@"commenté peut être utilisé. Voici quelques autres idées (au final pas si différentes cependant).
Supposons que vous vouliez faire ceci:
find . -exec cmmnd … {} +
(où …désigne des arguments constants). Découvrez quel cmmndest vraiment le chemin absolu . Par exemple, cela peut être /bin/cmmnd. Ensuite, exécutez quelque chose comme ceci:
find . -exec /aaa/zzzzz … {} +
où /aaa/zzzzzest une commande inexistante dont le nom est de la même longueur que /bin/cmmnd. Maintenant find, construira des lignes de commande avec /aaa/zzzzzqui seront de la même longueur que les lignes de commande avec /bin/cmmnd. Tu auras
find: '/aaa/zzzzz': No such file or directory
une ou plusieurs fois. Comptez-les pour obtenir le nombre souhaité. Cette approche simple:
find . -exec /aaa/zzzzz … {} + 2>&1 | wc -l
n'est pas le meilleur car il findpeut également imprimer par exemple permission deniedpour certains fichiers qu'il rencontre. Mais si vous créez /aaa/zzzzzun exécutable valide qui imprime exactement une ligne (cela peut être une ligne vide), cela devrait fonctionner:
find . -exec /aaa/zzzzz … {} + | wc -l
Une autre amélioration consiste à nommer l'outil /a(au lieu de /aaa/zzzzz) et à l'appeler comme /////aou /////////////////aetc., en fonction de la longueur dont vous avez besoin. Exemple:
find . -exec /////////a … {} + | wc -l
Pour être complet, voici à quoi apeut ressembler:
#!/bin/sh
echo
C'est presque comme notre wrapper sans cmmnd "$@", mais il utilise la sortie standard.
Remarques:
Le nombre exact de
/caractères n'est pas critique. Une erreur de quelques-uns ne changera pas radicalement le résultat . Si vous avez besoin d'un résultat d' estimation , vous pouvez utiliser aveuglément///////////aenviron, à moins que le chemin vers le necmmndsoit anormalement long. Notez qu'utiliser exactement/avous donnera la limite inférieure.En pratique, vous avez souvent d'autres tests avant
-exec cmmnd … {} +. Si vous remplacezcmmndpar/////////aenviron, les autres tests seront toujours effectués. Vous ne devez pas les omettre car ils décident-execen premier lieu vers quels chemins d'accès . Mais si les tests font ou changent quelque chose, il se peut que les exécuter sans le soitcmmndest faux.Par exemple, vous pouvez supprimer des fichiers avec
-delete -exec cmmnd … {} +, oùcmmndgénère un rapport sur les fichiers qui ont été supprimés. Dans ce cas, l'utilisation/////////asupprimera les fichiers sans générer de rapport. Alors réfléchissez avant d'agir.Assurez-vous que tests / actions / quoi que ce soit d'autre que de
-exec /////////a … {} +n'imprimer rien sur stdout. Ou laissez/autiliser un autre canal.Le traitement des arborescences de répertoires et la réalisation des (autres) tests peuvent prendre un certain temps, même sans
cmmnd(s).
Eh bien, le texte standard dit:
La taille de tout ensemble de deux ou plusieurs chemins d'accès doit être limitée de telle sorte que l'exécution de l'utilitaire ne provoque pas le dépassement de la limite {ARG_MAX} du système.
Il ne faut donc pas créer une liste d'arguments trop volumineuse pour être exécutée. Cela irait à l'encontre de l'intérêt d'une fonctionnalité comme celle-ci.
Le nombre d'appels qu'il fait exactement dépend de l'implémentation et c'est probablement quelque chose dont vous ne devriez pas trop vous soucier. La norme promet que les invocations de la même -execclause ne se chevauchent pas, ce qui peut être pertinent pour l'exactitude si vous exécutez quelque chose qui a un état extérieur.
Cependant, sous Linux, la taille maximale réelle des arguments de ligne de commande est basée sur la taille de la pile et peut être modifiée indirectement avec ulimit -s. Et il semble que contrairement par exemple xargs, findsur mon Debian et Ubuntu ne vérifie pas réellement la limite à l'exécution, il est donc théoriquement possible de rencontrer des problèmes.
$ mkdir bar $ touch bar/{00000..99999}
$ ulimit -Ss 512 $ getconf ARG_MAX
131072
$ find bar -type f -exec sh ./args.sh {} +
find: ‘sh’: Argument list too long
find: ‘sh’: Argument list too long
...
Cependant, la valeur par défaut pour ulimit -sest 8192, vous ne risquez donc pas de rencontrer ce problème, sauf sur un système très contraint.