60 votes

Comment faire pour que find échoue si -exec échoue ?

Lorsque je lance cette commande dans le shell (dans un répertoire non vide) :

find . -exec invalid_command_here {} \;

Je comprends :

find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory

(et ainsi de suite pour chaque fichier)

J'ai besoin find pour échouer après la première erreur. Y a-t-il un moyen de faire en sorte que cela fonctionne ? Je ne peux pas utiliser xargs car j'ai des espaces dans mon chemin, mais j'ai besoin que le script qui l'appelle renvoie un code d'erreur.

0 votes

54voto

staffan Points 3299

Il s'agit d'une limitation de find . Le site Norme POSIX spécifie que le statut de retour de find vaut 0 sauf si une erreur s'est produite lors du parcours des répertoires ; le statut de retour des commandes exécutées n'entre pas en ligne de compte.

Vous pouvez faire en sorte que les commandes écrivent leur état dans un fichier ou dans un descripteur :

find_status_file=$(mktemp findstatus)
: >"$find_status_file"
find … -exec sh -c 'trap "echo \$?" EXIT; invalid_command "$0"' {} \;
if [ -s "$find_status_file" ]; then
  echo 1>&2 "An error occurred"
fi
rm -f "$find_status_file"

Une autre méthode, comme vous l'avez découvert est d'utiliser xargs. Le site xargs traite toujours tous les fichiers, mais renvoie le statut 1 si l'une des commandes renvoie un statut non nul.

find … -print0 | xargs -0 -n1 invalid_command

Une autre méthode consiste à éviter find et utiliser le globbing récursif dans le shell à la place : **/ signifie une profondeur quelconque de sous-répertoires. Cela nécessite la version 4 ou supérieure de bash ; MacOS est bloqué à la version 3.x, vous devrez donc l'installer à partir d'un catalogue de logiciels portés. Utilisez set -e pour arrêter le script à la première commande retournant un état non nul.

shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done

Attention, dans bash 4.0 à 4.2, cela fonctionne mais traverse des liens symboliques vers des répertoires, ce qui n'est généralement pas souhaitable.

Si vous utilisez zsh au lieu de bash, le globbing récursif fonctionne sans problème. Zsh est disponible par défaut sur OSX/MacOS. Dans zsh, vous pouvez simplement écrire

set -e
for x in **/*.xml; do invalid_command "$x"; done

0 votes

Le site xargs L'approche fonctionne en général mais s'arrête d'une manière ou d'une autre sur bash -c des commandes. Par exemple : find . -name '*.xml' -print0 | xargs -0 -n 1 -I '{}' bash -c "foo {}" . Cette opération est exécutée plusieurs fois alors que find . -name '2*.xml' -print0 | xargs -0 -n 1 -I '{}' foo {} est exécuté une fois et échoue. Une idée de la raison ?

3 votes

@DKroot Ne jamais utiliser {} à l'intérieur de bash -c . Ceci prend le nom du fichier et l'insère directement dans la commande shell. Si le nom du fichier contient des caractères qui ont une signification spéciale dans l'interpréteur de commandes, tels que des espaces, l'interpréteur de commandes interprète ces caractères spéciaux comme tels. Si vous avez besoin d'un shell, passez {} comme un argument séparé, par exemple bash -c 'foo "$0"' {} (notez les guillemets autour de $0 également).

0 votes

OK, questions de citations mises à part, pourquoi la suite ne s'arrête pas à la première erreur ? find . -name '*' -print0 | xargs -0 -n 1 -I '{}' bash -c 'foo "$0"' {}

26voto

Steven Fisher Points 2201

Je peux utiliser ça à la place :

find . -name *.xml -print0 | xargs -n 1 -0 invalid_command

25voto

Swiss Points 261

xargs est une option. Cependant, il est en fait trivialement facile de faire ceci avec find également en utilisant + au lieu de \;

-exec  utility_name  [argument ...]   {} +

De la Documentation POSIX :

Si l'expression primaire est ponctuée par un signe plus, l'expression primaire est toujours évaluée comme vraie, et les noms de chemin pour lesquels l'expression primaire est évaluée sont regroupés dans des ensembles. L'utilitaire nom_utilitaire est invoqué une fois pour chaque ensemble de noms de chemin agrégés. Chaque invocation doit commencer après l'agrégation du dernier nom de chemin de l'ensemble, et doit être terminée avant la sortie de l'utilitaire find et avant l'agrégation du premier nom de chemin de l'ensemble suivant (le cas échéant) pour cette primaire, mais il n'est pas précisé si l'invocation a lieu avant, pendant ou après les évaluations des autres primaires. Si une invocation renvoie une valeur non nulle comme état de sortie, l'utilitaire find doit renvoyer un état de sortie non nul. Un argument ne contenant que les deux caractères "{}" est remplacé par l'ensemble des noms de chemin agrégés, chaque nom de chemin étant passé comme un argument distinct à l'utilitaire invoqué dans l'ordre où il a été agrégé. La taille de tout ensemble de deux noms de chemin ou plus doit être limitée de telle sorte que l'exécution de l'utilitaire n'entraîne pas le dépassement de la limite {ARG_MAX} du système. Si plus d'un argument contenant uniquement les deux caractères "{}" est présent, le comportement est non spécifié.

0 votes

Cela fonctionne pour moi, cela ne devrait-il pas être la bonne réponse ?

2 votes

@Higgs Sur stackoverflow, 90% du temps, être le premier est tout ce qui compte.

3 votes

@Higgs et @Swiss - Cela fonctionnera pour certains cas d'utilisation, mais cela nécessite l'utilisation de l'outil de gestion de l'information. -exec ed pour accepter des arguments multiples (dans ce cas, tous les éléments trouvés par la commande find ), puisque c'est ce que l + L'opérateur le fait. Il collecte les noms des nœuds, puis exécute -exec avec tous ces noms comme arguments. Ce qui, si je ne me trompe pas, pourrait dépasser le nombre maximum d'arguments possibles dans le shell, pour une part, et nécessite également, là encore, le support du script ou programme invoqué.

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X