234 votes

find -exec', une fonction shell sous Linux

Y a-t-il un moyen d'obtenir find pour exécuter une fonction que je définis dans le shell ?

Par exemple :

dosomething () {
  echo "Doing something with $1"
}
find . -exec dosomething {} \;

Le résultat de tout cela est :

find: dosomething: No such file or directory

Y a-t-il un moyen d'obtenir find 's -exec pour voir dosomething ?

299voto

Adam Rosenfield Points 176408

Comme seul l'interpréteur de commandes sait comment exécuter les fonctions de l'interpréteur de commandes, vous devez exécuter un interpréteur de commandes pour exécuter une fonction. Vous devez également marquer votre fonction pour l'exportation avec export -f sinon le sous-shell n'en héritera pas :

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;

7 votes

Tu m'as battu. Au fait, vous pouvez mettre les accolades à l'intérieur des guillemets au lieu d'utiliser $0 .

0 votes

@Dennis : Ah, bien vu, je pensais que {} L'argument de trouver devait être un mot séparé, mais apparemment non.

1 votes

@alxndr : Vous pouvez obtenir un texte monospace dans les commentaires en mettant des backticks (`) autour de votre texte.

145voto

Jac Points 247
find . | while read file; do dosomething "$file"; done

12 votes

Bonne solution. Elle ne nécessite pas d'exporter la fonction ou de s'embrouiller avec l'échappement des arguments et est probablement plus efficace puisqu'elle ne génère pas de sous-shells pour exécuter chaque fonction.

24 votes

Gardez à l'esprit, cependant, qu'il s'arrêtera sur les noms de fichiers contenant des nouvelles lignes.

4 votes

C'est plus "shell'ish" car vos variables et fonctions globales seront disponibles, sans créer un nouveau shell/environnement à chaque fois. J'ai appris cela à la dure après avoir essayé la méthode d'Adam et rencontré toutes sortes de problèmes d'environnement. Cette méthode ne corrompt pas non plus le shell de votre utilisateur actuel avec toutes les exportations et nécessite moins de dicipline.

37voto

pajamian Points 1

Réponse de Jac est formidable, mais il comporte quelques écueils qui peuvent être facilement surmontés :

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

Il utilise null comme délimiteur au lieu d'un saut de ligne, donc les noms de fichiers avec des sauts de ligne fonctionneront. Elle utilise également la fonction -r qui désactive l'échappement des backslashs, et sans lui les backslashs dans les noms de fichiers ne fonctionneront pas. Il efface également IFS afin que les éventuels espaces blancs de fin de nom ne soient pas supprimés.

2 votes

C'est bon pour /bin/bash mais ne fonctionnera pas dans /bin/sh . Ce qui est dommage.

0 votes

@ Quelle chance qu'au moins cela fonctionne dans /bin/bash.

22voto

Wagner Points 99

Ajouter des citations dans {} comme indiqué ci-dessous :

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

Cela corrige toute erreur due aux caractères spéciaux renvoyés par l'option find , par exemple les fichiers dont le nom contient des parenthèses.

4 votes

Ce n'est pas la façon correcte d'utiliser {} . Cela se brisera pour un nom de fichier contenant des guillemets doubles. touch '"; rm -rf .; echo "I deleted all you files, haha . Oups.

2 votes

Oui, c'est très mauvais. Il peut être exploité par des injections. Très dangereux. Ne l'utilisez pas !

1 votes

@kdubs : Utilisez $0 (sans guillemets) dans la chaîne de commande et passez le nom de fichier comme premier argument : -exec bash -c 'echo $0' '{}' \; Notez que lorsque vous utilisez bash -c , $0 est le premier argument, pas le nom du script.

9voto

Mike Maready Points 11

Demandez au script de s'appeler lui-même, en passant chaque élément trouvé comme argument :

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

Lorsque vous exécutez le script par lui-même, il trouve ce que vous recherchez et s'appelle lui-même en passant chaque résultat de recherche comme argument. Lorsque le script est exécuté avec un argument, il exécute les commandes sur l'argument et sort ensuite.

1 votes

Idée sympa mais mauvais style : utilisation du même script à deux fins. si vous voulez réduire le nombre de fichiers dans votre bin/, vous pourriez fusionner tous vos scripts en un seul qui aurait une grande clause case au début. solution très propre, n'est-ce pas ?

0 votes

Sans compter que cela échouera avec find: ‘myscript.sh’: No such file or directory si elle est lancée en tant que bash myscript.sh ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X