Як змусити `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це можливе рішення для злому.
barrycarter

Відповіді:


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;

Навколо цієї перевірки немає прапора або навколо функції, яка його викликає. Це, мабуть, пов’язане з max-документами, що, напевно, має сенс: якщо ви встановите max procs досить високо, це не буде турбувати перевірку, поки не буде досягнуто межі, яка, можливо, вам стане ніколи.

Кращим рішенням для того, що ви намагаєтесь зробити, може бути використання GNU Make :

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

all: $(TARGETS)

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

Потім:

$ make -k -j4 

матиме такий же ефект і надасть вам набагато кращий контроль.


9

Здавалося б, про один із найбільш очевидних розмов про це говорять лише інші пропозиції.

Тобто ви можете використовувати наступне:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

аби «примусити успіх».

Зауважте, це іде за пропозицією лорнікс (тільки не так багато слів).

У будь-якому випадку, оскільки це фактично ігнорує фактичний статус виходу з процесу, я б переконався, що ви хочете якось зберегти статус підпроцесу для післясмертового аналізу. Наприклад:

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

trueТут кілька надлишкова і тому це може бути краще записати в вигляді:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Оскільки ми, мабуть, хотіли б знати, коли не вдалося торкнутися файлу "невдало". Іншими словами, ми вже не ігноруємо провал, ми беремо до відома і продовжуємо.

І, розглядаючи рекурсивний характер цього питання, можливо, ми точно бачимо, чому xargs не робить ігнорування відмови легко. Оскільки це ніколи не є хорошою ідеєю - вам слід посилити обробку помилок у процесі, який ви розробляєте замість цього. Я вважаю, що це поняття більше притаманне самої "філософії Unix".

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


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'. Цього разу (без каламбуру) сон у кіллалі вбиває процес сну, але xargs продовжується.

Ви б передали час виводу в / dev / null, і це зробить саме те, що ви шукаєте, без істотного перезапису існуючого процесу.

Я уявляю, якби я на мить задумався, я міг би придумати іншу програму, щоб зробити те ж саме без більш жорсткого базікання з "/ usr / bin / time" Або навіть написати сам, це просто похідна форка (або exec ()).

Не забудьте використовувати "/ usr / bin / time", оскільки я не впевнений, що вбудований "time" від bash зробить те саме "поїдання" сигналу.


1
Хорошою альтернативою timeдля цієї мети буде env, оскільки все, що вона робиться, - це додати нуль або більше необов'язкових змінних до середовища програми, яку вона запускає. Він не видає власного виводу, і код повернення викликаної програми буде переданий назад до того, що викликається env.
Джеймс Снерінгер

{Chuckle} Я подумав про це через деякий час, коли я це написав. Час - це перше, що прийшло в голову як команда «запустити щось». Хоча добре, хоча. Вітаю і дякую.
lornix

2

Ні для мене, timeні envдля мене не працювали (вони передають значення повернення своєї дитячої програми), тому я написав bliss:

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

потім chmod u+x ~/bliss

і щось подібне find_or_similar | xargs ~/bliss fatally_dying_program.sh

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