如何使`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子进程死后停止处理更多输入,而是继续处理吗?


我正在使用xargs4.4.2版,debian wheezy即使我杀死了特定sleep进程,看起来一切正常。xargs您使用的是哪个版本?可能是他们已解决了最新版本的问题。
Kannan Mohan 2014年

派对晚了一点,但是怎么样xargs ... bash -c '...;exit 0'甚至xargs ... bash -c '... || echo erk'
Samveen'Apr 14'17

请注意,这parallel -j 1是一个可能的破解解决方案。
barrycarter

Answers:


25

不,你不能。从xargssavannah.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 

将具有相同的效果,并为您提供更好的控制。


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哲学”本身中更为固有。

最后,我想这也是James Youngman所建议的暗示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”。这次(无双关语),killall sleep杀死了睡眠过程,但是xargs继续。

您可以将时间的输出通过管道传递到/ dev / null,这将完全满足您的期望,而无需大量重写现有过程。

我想如果我想一会儿,我可以想出另一个程序来执行同样的操作,而无需来自'/ usr / bin / time'的stderr震颤。甚至自己写一个,它只是一个“ fork”(或exec()派生)。

请记住要使用“ / usr / bin / time”,因为我不确定bash内置的“时间”是否会对信号产生相同的“影响”。


1
time为此目的的一个很好的选择是env,因为它所做的只是向其运行的程序环境添加零个或多个可选变量。它不会发出自己的输出,被调用程序的返回代码将被传递回给任何被调用的env
James Sneeringer

{咯咯笑}我写完这篇文章后不久就想到了。时间是作为“运行某事”命令想到的第一件事。效果很好。恭喜您,谢谢。
lornix

2

time没有env为我工作,也没有为我工作(他们传递了他们子程序的返回值),所以我写道bliss

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

然后 chmod u+x ~/bliss

和类似的东西 find_or_similar | xargs ~/bliss fatally_dying_program.sh

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.