Il s'agit d'une limitation de find
. La norme POSIX spécifie que l'état de retour find
est 0 sauf si une erreur s'est produite lors de la traversée des répertoires; l'état de retour des commandes exécutées n'y entre pas.
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"
Comme vous l'avez découvert , une autre méthode consiste à utiliser des xargs. Les xargs
commandes traitent toujours tous les fichiers, mais renvoie l'état 1 si l'une des commandes renvoie un état différent de zéro.
find … -print0 | xargs -0 -n1 invalid_command
Une autre méthode consiste à éviter find
et à utiliser à la place le globbing récursif dans le shell: **/
signifie n'importe quelle profondeur de sous-répertoires. Cela nécessite la version 4 ou supérieure de bash; macOS est bloqué à la version 3.x, vous devez donc l'installer à partir d'une collection de ports. Permet set -e
d'arrêter le script sur la première commande renvoyant un état différent de zéro.
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 dès la sortie de la boîte sans gotchas. Zsh est disponible par défaut sur OSX / macOS. En zsh, vous pouvez simplement écrire
set -e
for x in **/*.xml; do invalid_command "$x"; done
xargs
approche fonctionne en général mais casse en quelque sorte sur lesbash -c
commandes. Par exemple:find . -name '*.xml' -print0 | xargs -0 -n 1 -I '{}' bash -c "foo {}"
. Ceci est exécuté plusieurs fois tandis qu'ilfind . -name '2*.xml' -print0 | xargs -0 -n 1 -I '{}' foo {}
est exécuté une fois et échoue. Une idée pourquoi?