web-dev-qa-db-fra.com

trouver -exec une fonction shell sous Linux?

Existe-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 est:

find: dosomething: No such file or directory

Existe-t-il un moyen d’obtenir le -exec de find pour voir dosomething?

150
alxndr

Puisque seul le shell sait comment exécuter les fonctions du shell, vous devez exécuter un shell pour exécuter une fonction. Vous devez également marquer votre fonction pour l'exportation avec export -f, sinon le sous-shell ne les héritera pas:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
213
Adam Rosenfield
find . | while read file; do dosomething "$file"; done
91
Jac

Ajoutez des guillemets dans {} comme indiqué ci-dessous:

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

Ceci corrige toute erreur due aux caractères spéciaux renvoyés par find, , Par exemple des fichiers avec des parenthèses dans leur nom.

17
Wagner

La réponse de Jak ci-dessus est excellente, mais présente quelques pièges faciles à surmonter:

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

Ceci utilise null comme délimiteur au lieu d'un saut de ligne, les noms de fichiers avec sauts de ligne fonctionneront. Il utilise également l'indicateur -r qui désactive l'échappement de barre oblique inversée. Sans barres obliques inverses dans les noms de fichiers, cela ne fonctionnera pas. Il efface également IFS de sorte que les espaces blancs de fin éventuels dans les noms ne soient pas ignorés.

14
pajamian

Demandez au script de s’appeler lui-même, en passant chaque élément trouvé en 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 seul, il trouve ce que vous recherchez et s’appelle lui-même en transmettant chaque résultat de recherche comme argument. Lorsque le script est exécuté avec un argument, il exécute les commandes de l'argument, puis se ferme.

8
Mike Maready

Traitement des résultats en vrac

Pour une efficacité accrue, de nombreuses personnes utilisent xargs pour traiter les résultats en bloc, mais cela est très dangereux. À cause de cela, une autre méthode introduite dans find qui exécute les résultats en bloc. 

Notez cependant que cette méthode peut être assortie de certaines mises en garde, comme par exemple une exigence dans POSIX -find d’avoir {} à la fin de la commande.

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

find transmettra de nombreux résultats en tant qu'arguments à un seul appel de bash et la for- boucle itérera à travers ces arguments, en exécutant la fonction dosomething sur chacun de ceux-ci.

La solution ci-dessus commence les arguments à $1, raison pour laquelle il existe un _ (qui représente $0).

Traitement des résultats un par un

De même, je pense que la réponse la plus acceptée devrait être corrigée pour être

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

Cela est non seulement plus sain, car les arguments doivent toujours commencer par $1, mais l'utilisation de $0 pourrait également entraîner un comportement inattendu si le nom du fichier renvoyé par find avait une signification particulière pour le shell.

7
Dominik

Pour ceux d'entre vous qui recherchent une fonction bash qui exécutera une commande donnée sur tous les fichiers du répertoire courant, j'ai compilé une des réponses ci-dessus:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

Notez que cela rompt avec les noms de fichiers contenant des espaces (voir ci-dessous).

A titre d'exemple, prenons cette fonction:

world(){
    sed -i 's_hello_world_g' "$1"
}

Disons que je voulais changer toutes les occurrences de hello to world dans tous les fichiers du répertoire actuel. Je ferais:

toall world

Pour être en sécurité avec les symboles figurant dans les noms de fichiers, utilisez: 

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(mais vous avez besoin d’une find qui gère -print0, par exemple, GNU find).

2
Jason Basanese

Je trouve le moyen le plus simple de suivre en répétant deux commandes en une seule

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done
1
edib

Il n'est pas possible d'exécuter une fonction de cette façon.

Pour résoudre ce problème, vous pouvez placer votre fonction dans un script shell et l’appeler à partir de find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

Maintenant, utilisez-le dans find comme:

find . -exec dosomething.sh {} \;
1
codaddict

Placez la fonction dans un fichier séparé et obtenez find pour l'exécuter.

Les fonctions du shell sont internes au shell dans lequel elles ont été définies; find ne pourra jamais les voir.

1
Angus

Pas directement, non. Find s’exécute dans un processus séparé, pas dans votre shell.

Créez un script Shell faisant le même travail que votre fonction et trouvez peut -exec that.

0
Laurence Gonsalves

Pour référence, j'évite ce scénario en utilisant: 

for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
  _script_function_call $i;
done;

Obtenez la sortie de find dans le fichier de script actuel et parcourez la sortie comme vous le souhaitez. Je suis d'accord avec la réponse acceptée, mais je ne veux pas exposer la fonction en dehors de mon fichier de script.

0
dinesh saini