Как заставить `xargs` игнорировать выход ребенка и продолжать обработку

24

Иногда я выполняю длинную xargsработу в одночасье, и действительно раздражает то, что утром он xargsумер где-то посередине, например, из-за ошибки сегментации в одном особом случае, как это произошло ночью.

Если хотя бы один xargsребенок убит, он больше не обрабатывает ввод:

Консоль 1:

[09:35:48] % seq 40 | xargs -i --max-procs=4 bash -c 'sleep 10; date +"%H:%M:%S {}";'
xargs: bash: terminated by signal 15
09:35:58 3
09:35:58 4
09:35:58 2
<Exit with code 125>

Консоль 2:

[09:35:54] kill 5601

Могу ли я как-то предотвратить xargsостановку обработки какого-либо ввода после смерти дочернего процесса и продолжения обработки?

Кристоф Вурм
источник
Я использую xargsверсию 4.4.2 debian wheezyи похоже, что все работает нормально, даже если я убиваю определенный sleepпроцесс. Какую версию xargsвы используете? возможно, они исправили проблему в последней версии.
Каннан Мохан,
Немного опоздал на вечеринку, но как насчет xargs ... bash -c '...;exit 0'или дажеxargs ... bash -c '... || echo erk'
Самвин
Обратите внимание, что parallel -j 1это возможное решение взломать.
Баррикартер

Ответы:

25

Нет, ты не можешь. Из xargsисточников на savannah.gnu.org :

if (WEXITSTATUS (status) == CHILD_EXIT_PLEASE_STOP_IMMEDIATELY)
  error (XARGS_EXIT_CLIENT_EXIT_255, 0,
         _("%s: exited with status 255; aborting"), bc_state.cmd_argv[0]);
if (WIFSTOPPED (status))
  error (XARGS_EXIT_CLIENT_FATAL_SIG, 0,
         _("%s: stopped by signal %d"), bc_state.cmd_argv[0], WSTOPSIG (status));
if (WIFSIGNALED (status))
  error (XARGS_EXIT_CLIENT_FATAL_SIG, 0,
         _("%s: terminated by signal %d"), bc_state.cmd_argv[0], WTERMSIG (status));
if (WEXITSTATUS (status) != 0)
  child_error = XARGS_EXIT_CLIENT_EXIT_NONZERO;

Там нет флага вокруг этой проверки или вокруг функции, которая ее вызывает. Похоже, это связано с макс. Процессорами, что, я полагаю, имеет смысл: если вы установите макс. Прокы достаточно высоко, проверка не будет беспокоить до тех пор, пока не достигнет предела, которого вы никогда не получите.

Лучшим решением для того, что вы пытаетесь сделать, может быть использование GNU Make :

TARGETS=$(patsubst %,target-%,$(shell seq 1 40))

all: $(TARGETS)

target-%:
    sleep 10; date +"%H:%M:%S $*"

Затем:

$ make -k -j4 

будет иметь тот же эффект, и даст вам гораздо лучший контроль.

ckhan
источник
9

Казалось бы, один из самых очевидных разговорных выражений упоминается только в других предложениях.

То есть вы можете использовать следующее:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

для того, чтобы «форсировать успех».

Обратите внимание, что это соответствует предложению lornix (просто не так много слов).

В любом случае, поскольку это фактически игнорирует фактическое состояние завершения процесса, я бы позаботился о том, чтобы вы каким-то образом сохранили статус подпроцесса для последующего анализа. Например:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed; (true)'

trueЗдесь несколько избыточна и поэтому это может быть лучше записать в виде:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Так как мы, вероятно, хотели бы знать, когда «неудачный» файл не может быть затронут. Другими словами, мы больше не игнорируем ошибку, мы принимаем к сведению и продолжаем.

И, рассмотрев рекурсивный характер этой проблемы, возможно, мы увидим, почему именно xargs не облегчает игнорирование ошибок. Потому что это никогда не является хорошей идеей - вместо этого вы должны улучшить обработку ошибок в процессе, который вы разрабатываете. Однако я считаю, что это понятие более присуще самой «философии Unix».

Наконец, я полагаю, что это то, на что ссылается Джеймс Янгман, рекомендуя trap, что, вероятно, можно было бы использовать аналогичным образом. То есть, не игнорируйте проблему ... заманите ее в ловушку и решите ее, или вы проснетесь однажды и обнаружите, что ни одна из подпрограмм не преуспела вообще ;-)

kingofephyra
источник
3

Используйте trap:

$ seq 40 | xargs -i --max-procs=4 bash -c \
 'trap "echo erk; exit 1" INT TERM;  sleep 10; date +"%H:%M:%S {}";' fnord
16:07:39 2
16:07:39 4
erk
16:07:39 1
^C
erk
erk
erk
erk

Также можно переключиться с оболочки на другой язык, на котором вы также можете установить обработчики сигналов.

Также обратите внимание, что после bash -c foo..вы должны указать значение, которое $0должно принимать (здесь fnord), чтобы первое слово, полученное с помощью seq, не было съедено.

Джеймс Янгман
источник
2

Поместите туда другую команду, чтобы «съесть» сигнал от умирающей программы.

Я попробовал ваш пример, изначально как показано, чтобы доказать проблему ... killall sleep убивает процесс сна, прерывает bash и завершает работу xargs.

В качестве теста я вставил команду типа «запустить другую команду» между xargs и bash ... в данном случае «/ usr / bin / time». на этот раз (без каламбура), killall sleep убивает процесс сна, но xargs продолжается.

Вы бы перенаправили вывод времени в / dev / null, и это сделало бы именно то, что вы ищете, без существенной перезаписи существующего процесса.

Я представляю, если бы я задумался на мгновение, я мог бы придумать другую программу, которая сделала бы то же самое без болтовни stderr из '/ usr / bin / time'. Или даже сам напишите, это просто «вилка» (или производная exec ()).

Не забудьте использовать «/ usr / bin / time», так как я не уверен, что встроенное «time» из bash будет делать то же самое «поедание» сигнала.

lornix
источник
1
Хорошей альтернативой timeдля этой цели будет env, поскольку все, что он делает, - это добавляет ноль или более необязательных переменных в среду программы, которую он запускает. Он не выдает собственных выходных данных, и код возврата вызванной программы будет передан обратно к тому, что вызвано env.
Джеймс Снерингер
{Смешок} Я подумал об этом через некоторое время после того, как написал это. Время было первым, что пришло в голову как команда «запустить что-то». Работает хорошо, хотя. Поздравляю и спасибо.
lornix
2

Ни то, timeни другое не envработало для меня (они передают возвращаемое значение своей дочерней программы), поэтому я написал bliss:

#!/bin/sh
"$@"
exit 0

тогда chmod u+x ~/bliss

и что-то вроде find_or_similar | xargs ~/bliss fatally_dying_program.sh

пикс
источник