Comment intégrer la commande mv après la commande find?

61

Je cherche des fichiers dont le nom contient AAAdans leur chemin à l'aide de la commande suivante:

find path_A -name "*AAA*"

Étant donné le résultat affiché par la commande ci-dessus, je souhaite déplacer ces fichiers dans un autre chemin, par exemple path_B. Au lieu de déplacer ces fichiers un par un, puis-je optimiser la commande en déplaçant ces fichiers juste après la commande find?

huahsin68
la source

Réponses:

87

Avec GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Cela utilisera l' -execoption de recherche qui remplacera le résultat {}de chaque recherche et exécutera la commande que vous lui donnerez. Comme expliqué dans man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

Dans ce cas, nous utilisons la +version de -execafin de pouvoir exécuter le moins d’ mvopérations possible:

   -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 invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.
cuonglm
la source
N'oubliez pas le "\;" à la fin de l'exec!
Charles Roth
@Charles Roth: c'est ce +qui fait le travail, vous pouvez lire ma citation ci-dessus ou man findplutôt
cuonglm
Merci pour cela! J'essayais d'utiliser -exec mv {} path_b +et cela échouait avec des erreurs d'autorisations. TBH, je ne comprends toujours pas pourquoi, mais ça -exec mv -t path_b {} +marche!
Jeremy Davis
6
@JeremyDavis Lors de l'utilisation -exec ... {} +, le {}doit être la dernière chose avant le +. C'est pourquoi il utilise mv -t destdir {} +et non mv {} destdir +. Un rhume a utilisé à la -exec mv {} destdir ';'place, mais cela aurait été exécuté mvune fois pour chaque fichier.
Kusalananda
23

Vous pouvez aussi faire quelque chose comme ci-dessous.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Où,

  1. -0S'il y a des espaces vides ou des caractères (y compris les nouvelles lignes), de nombreuses commandes ne fonctionneront pas. Cette option prend en charge les noms de fichiers avec un espace vide.
  2. -IRemplacez les occurrences de replace-str dans les arguments initiaux par des noms lus à partir de l'entrée standard. De plus, les espaces non cités ne terminent pas les éléments en entrée; au lieu de cela, le séparateur est le caractère de nouvelle ligne.

Essai

J'ai créé deux répertoires en tant que sourcediret destdir. Maintenant, j'ai créé un tas de fichiers à l'intérieur en sourcedirtant que file1.bak, file2.baketfile3 with spaces.bak

Maintenant, j'ai exécuté la commande en tant que,

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Maintenant, à l'intérieur du destdir, quand je le faisais ls, je pouvais voir que les fichiers étaient passés de sourcedirà destdir.

Références

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Ramesh
la source
22

Pour aider les utilisateurs d’OS X à répondre à cette question, la syntaxe sous OS X est légèrement différente. En supposant que vous ne souhaitiez pas effectuer de recherche récursive dans les sous-répertoires de path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Si vous souhaitez rechercher tous les fichiers de manière récursive dans path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
mannykary
la source
Ce n'est pas une syntaxe spécifique à OS X, c'est la même chose pour toutes celles findque j'ai utilisées. Bons points: -maxdepth(surtout si path_B est un sous-répertoire - évite d’ mvessayer de déplacer des fichiers déjà là!) Et d’utiliser \; (donc {} ne doit pas nécessairement être le dernier paramètre et la mvsyntaxe normale peut être utilisée)
drevicko
6

Le -execest le meilleur moyen de le faire. Si, pour une raison quelconque, ce n'est pas une option, vous pouvez également lire les résultats dans une boucle:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

C'est le moyen le plus sûr, il peut traiter des noms de fichiers contenant des espaces, des lignes nouvelles ou d'autres caractères étranges. Une méthode plus simple, mais qui échoue à moins que vos noms de fichiers ne soient composés que de simples caractères alphanumériques , est la suivante:

mv $(find path_A -name "*AAA*") path_B

Mais utilisez la boucle while.

terdon
la source
La méthode la plus simple peut toujours échouer si your file names consist only of simple alphanumeric characters, par exemple, si vous atteignezARG_MAX