26

I sometimes run long xargs jobs overnight and it is really annoying to discover in the morning that xargs died somewhere in the middle, for example because of a segmentation fault in one single special case, as happened this night.

If even one xargs child is killed, it does not process any more input:

Console 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>

Console 2:

[09:35:54] kill 5601

Can I somehow prevent xargs from stopping to process any more input once a child process dies and instead continue processing?

3
  • I am using xargs version 4.4.2 in debian wheezy and it looks like every things runs fine even if i kill a specific sleep process. Which version of xargs are you using ? may be they have fixed the issue in latest version. Commented Nov 24, 2014 at 3:58
  • A little late to the party, but how about xargs ... bash -c '...;exit 0' or even xargs ... bash -c '... || echo erk'
    – Samveen
    Commented Apr 14, 2017 at 4:33
  • Note that parallel -j 1 is a possible hack solution.
    – user2267
    Commented Jan 3, 2018 at 16:16

5 Answers 5

24

No, you can't. From the xargs sources at 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;

There's no flag around that check, or around the function that calls it. It does seem to be related to max procs, which I suppose makes sense: if you set max procs high enough, it won't bother checking until it's hit the limit, which you might get to be never.

A better solution for what you're trying to do might be to use GNU Make:

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

all: $(TARGETS)

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

Then:

$ make -k -j4 

will have the same effect, and give you much better control.

0
11

It would seem one of the most obvious colloquialisms is only alluded to by other proposals.

That is, you can use the following:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

in order to "force success".

Note, this is along the lines of the proposal by lornix (just not in so many words).

Anyway, since this is effectively ignoring the actual process exit status, I'd make sure you consider somehow saving the sub-process status for post-mortem analysis. E.g.:

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

The true here is somewhat redundant and so this may be better written as:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Since we probably would like to know when the 'failed' file couldn't be touched. In other words, we're no longer ignoring the failure, we're taking note and continuing.

And, after considering the recursive nature of this issue, perhaps we see exactly why xargs does not make ignoring failure easy. Because it is never a good idea - you should be enhancing the error handling within the process you're developing instead. I believe this notion, however, is more inherent in the "Unix philosophy" itself.

Finally, I suppose this is also what James Youngman alludes to by recommending trap, which presumably could be used in a similar manner. That is, don't ignore the problem... trap it and handle it or you make wake up one day and find that none of the sub-programs succeeded at all ;-)

4

Use 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

Alternatively switch from shell to another language in which you can also set signal handlers.

Note also that after bash -c foo.. you should specify the value that $0 should take (here, fnord) so that the first word produced by seq doesn't get eaten.

2

Put another command in there to 'eat' the signal from the dying program.

I tried your example, initally as shown to prove the problem... 'killall sleep' kills the sleep process, interrupts bash, and xargs quits.

As a test, I stuck a 'run another command' type command in between xargs and bash... in this case '/usr/bin/time'. this time (no pun), killall sleep kills the sleep process, but the xargs continues on.

You would pipe time's output to /dev/null, and this would do exactly what you're looking for without a major rewrite of your existing process.

I imagine if I ponder for a moment I could come up with another program to do the same without the stderr chatter from '/usr/bin/time'. Or even write one myself, it's just a 'fork' (or exec() derivative).

Remember to use '/usr/bin/time', as I'm not sure the built-in 'time' from bash will do the same 'eating' of the signal.

2
  • 1
    A good alternative to time for this purpose would be env, since all it does is add zero or more optional variables to the environment of the program it runs. It emits no output of its own, and the return code of the invoked program will be passed back up to whatever called env. Commented Jul 6, 2012 at 17:20
  • {Chuckle} I thought of that a while after I write this up. Time was the first thing that came to mind as a "run something" command. Works nicely though. Congrats and thank you.
    – lornix
    Commented Jul 6, 2012 at 17:26
2

Neither time nor env worked for me (they pass along the return value of their child program) so I wrote bliss:

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

then chmod u+x ~/bliss

and something like find_or_similar | xargs ~/bliss fatally_dying_program.sh

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .