Це обмеження 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 {}як виконується один раз і не працює. Будь-яка ідея чому?