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?{}
intérieurbash -c
. Cela prend le nom du fichier et l'insère directement dans la commande shell. Si le nom de fichier contient des caractères qui ont une signification spéciale dans le shell, tels que des espaces, le shell interprète ces caractères spéciaux comme tels. Si vous avez besoin d'un shell, passez{}
comme un argument séparé, par exemplebash -c 'foo "$0"' {}
(notez également les guillemets$0
).find . -name '*' -print0 | xargs -0 -n 1 -I '{}' bash -c 'foo "$0"' {}
find . -print0 | xargs -0 -n1 invalid_command
approche xargs ( ). Cela arrête le premier correctement erreur:find . -name '*' -print0 | xargs -0 -n 1 -I '{}' foo {}
. Génial! Mais la même approche ne fonctionne pas avecbash -c
(ci-dessus). La seule différence entre les deux estbash -c
.Je peux l'utiliser à la place:
la source
xargs
est une option. Cependant, il est en fait trivialement facile de le fairefind
également en utilisant+
au lieu de\;
De la documentation POSIX :
la source