Comment puis-je exécuter une commande spécifique pour chaque résultat de recherche?

49

Comment exécuter une commande spécifique pour chaque fichier trouvé à l'aide de la findcommande? Aux fins de la question, disons que je voudrais simplement supprimer chaque fichier trouvé par find.

FailedDev
la source

Réponses:

60

Edit: Bien que la réponse suivante explique le cas d'utilisation général, il convient de noter que la suppression de fichiers et de répertoires est un cas particulier. Au lieu d'utiliser la -execdir rm {} \;construction, utilisez simplement -delete, comme dans:

find -iname '*.txt' -delete

Cela gère un grand nombre de cas marginaux auxquels vous pourriez ne pas penser, notamment d'inclure les fichiers de commande et les répertoires à supprimer pour ne pas générer d'erreur. Pour d'autres cas d'utilisation ...

Le meilleur moyen de gérer les commandes en cours d'exécution des résultats d'une recherche consiste généralement à utiliser les différentes -execoptions de la findcommande. En particulier, vous devriez essayer d’utiliser -execdirautant que possible, car il se trouve dans le répertoire du fichier qui a été trouvé et est généralement plus sûr (dans le sens d’empêcher que des erreurs stupides soient désastreuses) par rapport aux autres options.

Les -execoptions sont suivies de la commande avec laquelle vous souhaitez exécuter {}indiquant le point où le fichier trouvé par find doit être inclus et sont terminées par \;exécuter la commande une fois pour chaque fichier ou +remplacer {}par une liste d'arguments de toutes les correspondances. . Notez que le point-virgule de terminaison est échappé de sorte que le shell ne le comprend pas comme un séparateur menant à une nouvelle commande.

Disons que vous avez trouvé tous les fichiers texte:

find -iname '*.txt' -execdir rm {} \;

Voici le bit pertinent du manuel de recherche ( 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.  The string ‘{}’
          is replaced by the current file name being processed  everywhere
          it occurs in the arguments to the command, not just in arguments
          where it is alone, as in some versions of find.  Both  of  these
          constructions might need to be escaped (with a ‘\’) or quoted to
          protect them from expansion by the shell.  See the EXAMPLES sec-
          tion for examples of the use of the -exec option.  The specified
          command is run once for each matched file.  The command is  exe-
          cuted  in  the starting directory.   There are unavoidable secu-
          rity problems surrounding use of the -exec  action;  you  should
          use the -execdir option instead.


   -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.


   -execdir command ;

   -execdir command {} +
          Like -exec, but the specified command is run from the  subdirec-
          tory  containing  the  matched  file,  which is not normally the
          directory in which you started find.  This a  much  more  secure
          method  for invoking commands, as it avoids race conditions dur-
          ing resolution of the paths to the matched files.  As  with  the
          -exec action, the ‘+’ form of -execdir will build a command line
          to process more than one matched file, but any given  invocation
          of command will only list files that exist in the same subdirec-
          tory.  If you use this option, you must ensure that  your  $PATH
          environment  variable  does  not  reference  ‘.’;  otherwise, an
          attacker can run any commands they like by leaving an  appropri-
          ately-named  file in a directory in which you will run -execdir.
          The same applies to having entries in $PATH which are  empty  or
          which are not absolute directory names.
Caleb
la source
La commande "find" fournie par Busybox ne prend pas en charge l'option -execdir; il peut donc être nécessaire d'utiliser l'une des méthodes pipe / xargs mentionnées ci-dessous.
MikeW
9

Une alternative consiste à diriger la sortie et à l’analyser avec les commandes suivantes. Le seul moyen sûr de le faire est d'utiliser l' -print0option, qui indique findd'utiliser un caractère nul comme délimiteur de résultats. Les commandes de réception doivent avoir la capacité de reconnaître une entrée délimitée par un zéro. Exemple:

find /home/phunehehe -iregex '.*\.png$' -print0 | xargs -0 file

Notez que l' -0option indique xargsde traiter l'entrée comme délimitée par null.

phunehehe
la source
Vous pouvez -executiliser plus de fichiers si vous le terminez au +lieu de ;. Voir la réponse de caleb.
Kevin
@ Kevin vous avez raison, j'ai mis à jour la réponse.
Phunehehe
3

Find a une commande de suppression intégrée si c'est tout ce que vous devez faire.

find . -name "*.txt" -delete

Tout fichier .txt trouvé sera supprimé à l'aide de la commande ci-dessus.

SaultDon
la source
2

Je cherchais une réponse à cette question et je suis tombé sur ce fil. Les réponses m'ont donné une idée sur la façon dont je pourrais y arriver. Supposons que vous souhaitiez trouver le mediainfofichier JPEG

Cela ajouterait mediainfo "au début et "à la fin de chaque fichier correspondant (pour éviter autant que possible les caractères spéciaux), mettez-le dans un script et exécutez-le:

find . -name *.jpg | sed -e 's/^/mediainfo "/g;' | sed -e 's/$/"/g;' > foo.sh && sh foo.sh

Si vous craignez que quelque chose ne se passe pas bien, vous pouvez éviter de rediriger la sortie vers un fichier et simplement voir le résultat dans le terminal avant d'exécuter le script.

RuMAN S
la source
1

Vous pouvez accomplir cela en utilisant la xargscommande. xargsexécute essentiellement une commande une fois pour chaque instruction de son entrée standard. Donc, si vous avez besoin de supprimer tous les .jpgfichiers d'un répertoire par exemple, voici une méthode rapide:

$ find ./ -name "*.jpg" | xargs rm 

Vous pouvez également utiliser le backtick (au-dessus du bouton Tab) pour le faire (notez qu'il s'agit du caractère backquote et non du guillemet simple):

$ rm `find ./ -name "*.jpg"`

Notez qu'en raison de la manière dont les xargsshells traitent leur entrée, la méthode xargs ne fonctionne que si aucun des noms de fichier et de répertoire impliqués ne contient d'espaces ni aucun \"'; la méthode backquote ne fonctionne que si aucun des noms de fichier et de répertoire impliqués ne contient d’espace ou d’espace vide \[?*.

IG83
la source
3
Ces deux méthodes sont potentiellement très dangereuses, en particulier la méthode du backtick. Il existe de nombreux problèmes potentiels liés aux caractères non-échappés dans les noms de fichiers qui pourraient entraîner la rupture de ces méthodes.
Caleb
1
Je vois ce que vous voulez dire, mais ces commandes peuvent aussi être utilisées avec d’autres outils que find, alors je pense qu’elles méritent d’être mentionnées ici.
IG83
Cela vaut peut-être la peine d’être mentionné, mais il est important de préciser quand ne pas les utiliser. La question de l'OP ici demandait spécifiquement de gérer la sortie de find, ce qui -execest généralement une meilleure solution. Si vous souhaitez les spécifier en tant que substituts, expliquez au moins comment utiliser cette fonction find -print0 | xargs -0pour gérer en toute sécurité les ruptures de noms de fichiers et expliquez-leur quand il convient de faire attention aux backticks.
Caleb
1
Au fait, bienvenue sur le site, je vois que c'est votre première réponse. Désolé de sauter partout. Il est important d'enseigner dès le départ aux gens les problèmes afin qu'ils ne commettent pas d'erreurs difficiles à détecter plus tard, mais je me souviens toujours du temps où je ne comprenais pas pourquoi c'était un si gros problème, alors, s'il vous plaît, don prenez pas la correction comme personnelle.
Caleb
3
Merci pour l'accueil! Bien sûr, il n'y a pas de ressentiment, vous avez certainement raison, le -exec est la manière appropriée de gérer cela. Je commence vraiment à voir à quel point cette plate-forme est formidable, vous apprenez de nouvelles choses même à partir des commentaires :)
IG83