Est-il possible d'imbriquer un 'find -exec' dans un autre 'find -exec'?

14

Quelque chose comme ce qui suit est ce que je cherche, mais mon code ne fonctionne pas, peu importe comment je m'échappe {}et+ ;

find ./ -maxdepth 1 -type d -name '.*' -exec \
    find {} -maxdepth 1 -type f -name '*.ini' -exec \
        md5sum \{\} \\; \;

Après avoir vu cette question Unix - & - Linux , j'ai trouvé que le code suivant fonctionne, mais il n'est pas imbriqué trouver en tant que tel, et je soupçonne qu'il existe une meilleure façon de faire ce travail particulier.

find ./ -maxdepth 1 -type d -name '.*' \
-exec bash -c 'for x; do
    find "$x" -maxdepth 1 -type f -name "*.ini" \
    -exec md5sum \{\} \;; \
done' _ {} \+

Existe-t-il un moyen d'imbriquer find -execsans avoir besoin d'invoquer un shell (comme ci-dessus), avec toutes ses citations délicates et ses contraintes d'échappement?

Ou cela peut-il être fait directement dans une seule commande find, en utilisant un mélange de ses nombreux paramètres?

Peter.O
la source
4
Bien qu'il soit possible de faire ce que vous demandez, lorsque les choses deviennent aussi complexes, je passe aux scripts shell ou Perl. Votre deuxième extrait de code fait à peu près cela, uniquement avec le script shell en ligne. Les one-liners héroïques sont divertissants, mais ils sont difficiles à comprendre et donc difficiles à maintenir. À moins que ce ne soit un accord unique que vous finissiez néanmoins par réussir, je ne vois pas de bonne raison de le faire autre que le défi intellectuel.
Warren Young
1
@Warren Young: Je ne pense certainement pas que le concept soit complexe, mais je suppose que vous voulez dire qu'il n'y a pas de moyen simple de faire avec find, mais si findvous ne pouvez pas le faire, alors pourquoi est-il findsi vénéré (?) Que l'outil- à utiliser pour trouver des fichiers? ... J'ai ensuite trouvé que cela find ./ -maxdepth 2 -path '.*/*.ini' -type f -exec md5sum {} \+fonctionnait bien dans ma situation (la référence de jw013 -prunem'a amené à cela dans la page de manuel), mais je me demande si c'est une méthode robuste (en général). Je n'ai jamais vraiment utilisé find(en moins d'un an de Linux) comme locatej'ai fait presque tout ce dont j'ai besoin, donc c'est un territoire inconnu.
Peter.O
1
Le -pathtest est exactement ce que j'allais suggérer. Avec cela, vous devriez pouvoir faire tout ce que vous voulez (désolé pour l'association Ace Of Base;))
rozcietrzewiacz

Réponses:

8

J'essaierais d'utiliser une seule recherche comme:

find .*/ -maxdepth 1 -type f -name '*.ini' -execdir md5sum {} +

ou même (pas finddu tout, juste un globbing de coquille)

md5sum .*/*.ini

bien que cela ne soit pas -type fcoché, cela ne fonctionne que si vous n'avez pas de répertoires / non-fichiers se terminant par .ini. Si vous le faites, vous pourriez utiliser

for x in .*/*.ini; do 
    if [ -f "$x" ]; then 
        md5sum "$x"
    fi
done

qui perdrait cependant l'avantage de n'avoir besoin que d'une seule invocation md5sum.

Éditer

Pour une méthode de chaînage générale et sûre find, vous pouvez faire quelque chose comme

find <paths> <args> -print0 | xargs -0 -I{.} find {.} <args for second find> [etc.]
jw013
la source
J'obtiens une erreur avec le -f(f?), Puis une autre erreur avec -execdir.. Quand je remplace -execdir par -exec, et / ou que je remplace également md5sum par print, je n'obtiens rien ..
Peter.O
Merci, pour l'alternative ... mais je suis plus à la recherche d'une façon de faire en utilisant find... Ce n'est pas tellement que je veux juste que cet exemple soit résolu, je cherche des informations sur les façons de trouver-fu .. . peut-être qu'il y a plus de peluches que fu dans find .. (je ne sais pas, parce que je ne l'ai pratiquement jamais utilisé), et c'est la première situation que je voulais vraiment utiliser (pour les fichiers image en fait) et le La fonctionnalité -exec dont j'ai tant entendu parler ne semble pas aussi puissante que son représentant (?) fait allusion à ... (+1 pour les alternatives, cependant) .. mais votre findexemple ne fonctionne tout simplement pas (encore )
Peter.O
J'obtiens cette erreur pour la findcommande: ... find: The relative path ~ / bin 'est inclus dans la variable d'environnement PATH, qui n'est pas sécurisée en combinaison avec l'action -execdir de find. Veuillez supprimer cette entrée de $ PATH` .... Alors peut-être que cela fonctionnera, mais je dois dire que j'essaie de me débarrasser de ~ / bin depuis un moment maintenant ... Je vais devoir en prendre plus regardez-le sérieusement ... je ne sais pas où je l'ai mis ... aucune idée où il se cache; le ~/bindans mon
CHEMIN
Je pense que le pouvoir de findest mieux apprécié dans les situations qui ne peuvent pas être entièrement réalisées avec des globes, mais comme ce genre de situations est rare, je n'ai normalement pas besoin de trouver grand-chose.
jw013
D'accord .. c'est un point intéressant et bon (à propos de l'utilisation de globes) ...
Peter.O
2

Votre problème d'origine ne nécessite pas d'appeler find récursivement mais je suppose que ce n'était pas le point.

Je pense qu'il n'est pas possible d'appeler find de manière récursive comme vous le souhaitez.

Ce qui suit n'appelle pas non plus la recherche récursivement (ou l'imbrication, quel que soit son nom), mais ne pouvez-vous pas simplement prendre un jeu de résultats de la première recherche et le nourrir dans la seconde? Voici comment je ferais instinctivement:

find `find ./ -maxdepth 1 -type d -name '.*'` \
    -maxdepth 1 -type f -name '*.ini' -exec md5sum {} \;

Vous pouvez également utiliser xargspour exécuter la deuxième recherche.

Mise à jour:

Je voulais ajouter que parce que la plupart des utilitaires UNIX prennent plusieurs arguments de nom de fichier au lieu d'un, vous pouvez généralement éviter le -exectout:

md5sum `find \`find ./ -maxdepth 1 -type d -name '.*'\` -maxdepth 1 -type f -name '*.ini'`

Lors de l'imbrication des contre-coups, il vous suffit d'ajouter des barres obliques inverses \avant celles intérieures.

Si nous imaginons que cela md5sumne prend qu'un seul argument de nom de fichier, nous pouvons toujours l'enrouler dans une forboucle:

for f in `find \`find ./ -maxdepth 1 -type d -name '.*'\` -maxdepth 1 -type f -name '*.ini'`
do
    md5sum $f
done

Notez que cela devient plus difficile si des noms de fichiers / répertoires commençant par -ou contenant un espace sont impliqués. Les utilitaires UNIX ne fonctionnent pas bien avec eux. Dans ce cas , l' ajout ./, --ou citations est nécessaire.

De toute évidence, l'exemple original n'est pas bon, car nous pourrions simplement faire:

md5sum .*/*.ini
casser
la source
1
Vous avez donné un bon aperçu de la situation, mais toutes les findméthodes affichées obtiennent une erreur lorsqu'un fichier / répertoire contient des espaces md5sum .*/*.ini... Cela fonctionne bien ... Je commence à avoir l'impression générale que * Warren Young * commenter les choses qui deviennent `` complexes '' findse produit tôt dans le jeu :), mais je suppose que cela findprend tout son sens lorsque les tests de condition sont plus complexes, mais en ce qui concerne l'imbrication -exec, j'ai assez bien abandonné l'idée, car il semble qu'il existe des moyens plus simples de le faire .. (mais perl n'est pas (encore) "simple" pour moi ...)
Peter.O
0

Au moins, j'ai réussi à imbriquer 2 commandes de recherche:

find ~ -maxdepth 1 -type d -name '.*' -execdir \
    find {} -maxdepth 1 -type f -name '*.ini' \;

Mais je n'ai pas résolu d'invoquer un autre appel -exec (dir) - à partir de là.

Utilisateur inconnu
la source
Oui, c'est exactement mon problème :)
Peter.O
Oui, mais ne pas imbriquer 3 n'est pas la même chose que ne pas imbriquer 2. :)
utilisateur inconnu