Як змусити знайти помилку, якщо -exec не працює?


29

Коли я запускаю цю команду в оболонці (у не порожньому каталозі):

find . -exec invalid_command_here {} \;

Я отримую це:

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

(і так далі для кожного файлу)

Мені потрібно findпісля першої помилки. Чи є спосіб змусити це працювати? Я не можу використовувати xargs, оскільки у мене є пробіли, але мені потрібен сценарій, який викликає це, щоб повернути код помилки.

Відповіді:


34

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

@DKroot Ніколи не використовуйте {}всередину bash -c. Це займає ім'я файлу та вставляє його безпосередньо всередині команди оболонки. Якщо ім'я файлу містить символи, які мають особливе значення в оболонці, такі як пробіли, оболонка інтерпретує ці спеціальні символи як такі. Якщо вам потрібна оболонка, подайте {}як окремий аргумент, наприклад bash -c 'foo "$0"' {}(також зазначте подвійні лапки $0).
Жил "ТАК - перестань бути злим"

Гаразд, цитуючи питання в стороні, чому наступне не зупиняється на першій помилці ?? find . -name '*' -print0 | xargs -0 -n 1 -I '{}' bash -c 'foo "$0"' {}
DKroot

@DKroot Чому вона зупиниться на помилці? xargs завжди виконує команду на всіх елементах.
Жил "ТАК - перестань бути злим"

Я намагаюся використовувати цю відповідь: find . -print0 | xargs -0 -n1 invalid_commandпідхід xargs ( ). Це припиняє на першій помилки правильно: find . -name '*' -print0 | xargs -0 -n 1 -I '{}' foo {}. Чудово! Але такий же підхід не працює bash -c(вище). Єдина різниця між ними bash -c.
DKroot


4

xargsє одним із варіантів. Однак насправді зробити це findтак само тривіально легко , використовуючи +замість цього\;

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

З документації POSIX :

Якщо первинний вираз визначається знаком плюс, первинний завжди оцінюється як істинне, а назви шляхів, за якими оцінюється первинне, об’єднуються у множини. Утиліта utility_name викликається один раз для кожного набору агрегованих імен шляхів. Кожне виклик починається після того, як прізвище останнього шляху в наборі агрегується, і завершується до виходу утиліти пошуку та перед тим, як ім'я першого шляху в наступному наборі буде агреговано для цього основного, але інакше не визначено, чи буде виклик виникає до, під час або після оцінювання інших праймеріз. Якщо будь-яке виклик повертає ненульове значення як статус виходу, утиліта find повертає ненульовий статус виходу.Аргумент, що містить лише два символи "{}", повинен бути замінений набором зведених імен шляхів, при цьому кожне ім'я шляху передається як окремий аргумент викликаній утиліті в тому ж порядку, в якому він був агрегований. Розмір будь-якого набору з двох і більше імен шляхів повинен бути обмежений таким чином, що виконання утиліти не призведе до перевищення ліміту {ARG_MAX} системи. Якщо є більше одного аргументу, що містить лише два символи "{}", поведінка не визначено.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.