web-dev-qa-db-fra.com

Pourquoi trouver -exec mv {} ./target/ + ne fonctionne-t-il pas?

Je veux savoir exactement quoi {} \; et {} \+ et | xargs ... faire. S'il vous plaît clarifier ces avec des explications.

Ci-dessous, trois commandes sont exécutées et produisent le même résultat, mais la première commande prend un peu de temps et le format est également légèrement différent.

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

C'est parce que 1st exécute la commande file pour chaque fichier provenant de la commande find. Donc, fondamentalement, il s’agit de:

file file1.txt
file file2.txt

Mais les 2 derniers trouvent avec -exec Les commandes exécutent la commande fichier une fois pour tous les fichiers, comme ci-dessous:

file file1.txt file2.txt

Ensuite, j’exécute les commandes suivantes sur lesquelles la première s’exécute sans problème, mais la seconde donne un message d’erreur.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

Pour commander avec {} \+, ça me donne le message d'erreur

find: missing argument to `-exec'

pourquoi donc? Quelqu'un peut-il s'il vous plaît expliquer ce que je fais mal?

96
Shahadat Hossain

Le page de manuel (ou le en ligne GNU manuel )) explique à peu près tout.

recherche -exec commande {} \;

Pour chaque résultat, command {} est exécuté. Toutes les occurrences de {} sont remplacés par le nom du fichier. ; est préfixé par une barre oblique pour empêcher le shell de l’interpréter.

recherche -exec commande {} +

Chaque résultat est ajouté à command et exécuté par la suite. En prenant en compte les limitations de longueur de commande, je suppose que cette commande peut être exécutée plusieurs fois, la page de manuel me prenant en charge:

le nombre total d'invocations de la commande sera bien inférieur au nombre de fichiers correspondants.

Notez cette citation de la page de manuel:

La ligne de commande est construite de la même manière que xargs construit ses lignes de commande

C'est pourquoi aucun caractère n'est autorisé entre {} et + sauf les espaces. + permet à find de détecter que les arguments doivent être ajoutés à la commande, exactement comme xargs.

La solution

Heureusement, l’implémentation de mv GNU peut accepter le répertoire cible en tant qu’argument, avec soit -t ou le paramètre plus long --target. Son utilisation sera:

mv -t target file1 file2 ...

Votre commande find devient:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

De la page de manuel:

-exec commande;

Exécuter la commande; true si le statut 0 est renvoyé. Tous les arguments suivants à trouver sont considérés comme des arguments de la commande jusqu'à un argument constitué de `; ' est rencontré. La chaîne "{}" est remplacée par le nom du fichier en cours de traitement, où qu'il se trouve dans les arguments de la commande, pas seulement dans les arguments où il est seul, comme dans certaines versions de find. Il peut être nécessaire d’échapper à ces deux constructions (avec un '\') ou de les citer pour les protéger de l’extension de Shell. Voir la section EXEMPLES pour des exemples d'utilisation de l'option -exec. La commande spécifiée est exécutée une fois pour chaque fichier correspondant. La commande est exécutée dans le répertoire de départ. Il y a des problèmes de sécurité inévitables liés à l'utilisation de l'action -exec; vous devriez plutôt utiliser l'option -execdir.

-exec commande {} +

Cette variante de l'action -exec exécute la commande spécifiée sur les fichiers sélectionnés, mais la ligne de commande est créée en ajoutant chaque nom de fichier sélectionné à la fin. le nombre total d'invocations de la commande sera bien inférieur au nombre de fichiers correspondants. La ligne de commande est construite de la même manière que xargs construit ses lignes de commande. Une seule instance de `{} 'est autorisée dans la commande. La commande est exécutée dans le répertoire de départ.

182
Lekensteyn

J'ai rencontré le même problème sur Mac OSX, en utilisant un ZSH Shell: dans ce cas, il n'y a pas de -t option pour mv, je devais donc trouver une autre solution. Cependant, la commande suivante a réussi:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

Le secret était pour citer les accolades. Pas besoin que les accolades soient à la fin de la commande exec.

J'ai testé sous buntu 14.04 (avec BASH et ZSH = coquilles), cela fonctionne de la même manière.

Cependant, lorsque vous utilisez le + signe, il semble en effet qu'il doit être à la fin de la commande exec.

5
arvymetal

L'équivalent standard de find -iname ... -exec mv -t dest {} + pour find implémentations qui ne supportent pas -iname ou mv implémentations qui ne supportent pas -t consiste à utiliser un shell pour réorganiser les arguments:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

En utilisant -name '*.[cC][pP][pP]', nous évitons également de nous fier aux paramètres régionaux actuels pour décider quelle est la version majuscule de c ou p.

Notez que +, contrairement à ; _ n’est spécial dans aucun shell, il n’a donc pas besoin d’être cité (bien que citer ne nuira pas, sauf bien sûr avec des shells comme rc qui ne supportent pas \ en tant qu'opérateur de cotation).

La fuite / dans /dest/dir/ est tel que mv échoue avec une erreur au lieu de renommer foo.cpp à /dest/dir _ dans le cas où un seul fichier cpp a été trouvé et /dest/dir n'existait pas ou n'était pas un répertoire (ou un lien symbolique vers un répertoire).

3
Stephane Chazelas