Це обмеження find
. Стандарт POSIX вказує, що стан повернення find
дорівнює 0, якщо не трапилася помилка під час проїзду каталогів; статус повернення виконаних команд не входить до нього.
Ви можете змусити команди писати їх статус у файл або дескриптор:
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"
Ще один метод, як ви виявили , - використовувати xargs. Ці xargs
команди завжди обробляє всі файли, але повертає стан 1 , якщо якийсь - або з команд повертає статус нуля.
find … -print0 | xargs -0 -n1 invalid_command
Ще один метод - це виключити find
та використовувати рекурсивне глобулювання замість оболонки: **/
означає будь-яку глибину підкаталогів. Для цього потрібна версія 4 або вище bash; macOS застряг у версії 3.x, тому вам доведеться встановити його з колекції портів. Використовуйте set -e
для зупинки сценарію першої команди, що повертає ненульовий статус.
shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done
Пам’ятайте, що в bash 4.0 до 4.2 це працює, але переходить символічні посилання на каталоги, що зазвичай не бажано.
Якщо ви використовуєте zsh замість bash, рекурсивна глобалізація працює поза коробкою без жодних домен. Zsh доступний за замовчуванням на OSX / macOS. В zsh можна просто писати
set -e
for x in **/*.xml; do invalid_command "$x"; done
xargs
Підхід працює в цілому , але як - то ламає наbash -c
команди. Наприклад:find . -name '*.xml' -print0 | xargs -0 -n 1 -I '{}' bash -c "foo {}"
. Це виконується кілька разів, тодіfind . -name '2*.xml' -print0 | xargs -0 -n 1 -I '{}' foo {}
як виконується один раз і не працює. Будь-яка ідея чому?