web-dev-qa-db-ja.com

`xargs`に子の出口を無視させ、さらに処理を続ける方法

私は時々長いxargsジョブを夜通し実行し、朝にxargsが途中でどこかで死亡したことを発見するのは本当に面倒です。今夜。

xargsの子が1つでも殺されると、それ以上の入力は処理されません。

コンソール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が停止して入力を処理できなくなり、代わりに処理を続行できますか?

25
Christoph Wurm

いいえ、できません。 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 procsに関連しているようです:max procsを十分に高く設定した場合、それが制限に達するまでチェックを行う必要はありません。

あなたがやろうとしていることのより良い解決策は GNU Make を使用することかもしれません:

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

all: $(TARGETS)

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

次に:

$ make -k -j4 

同じ効果があり、より優れた制御が可能になります。

25
ckhan

最も明白な口語主義の1つは、他の提案によってほのめかされているように見えます。

つまり、以下を使用できます。

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を推奨することで示唆しているものだと思います。これはおそらく同じように使用できると思われます。つまり、問題を無視しないでください...トラップして処理するか、ある日目を覚まし、サブプログラムがまったく成功しなかったことがわかります;-)

9
kingofephyra

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によって生成された最初のWordが食べられないようにすることにも注意してください。 。

3
James Youngman

timeenvも機能しなかったため(子プログラムの戻り値を渡す)、blissを作成しました。

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

次にchmod u+x ~/bliss

find_or_similar | xargs ~/bliss fatally_dying_program.shなど

2
pix

死にかけているプログラムからの信号を「食べる」ために別のコマンドをそこに入れます。

問題を証明するために、最初に示されているように、私はあなたの例を試しました... 'killall sleep'はスリーププロセスを強制終了し、bashに割り込み、xargsが終了します。

テストとして、xargsとbashの間に「run another command」タイプのコマンドを貼り付けました...この場合は「/ usr/bin/time」です。今回(しゃれはありません)、killall sleepはスリーププロセスを強制終了しますが、xargsは続行します。

時間の出力を/ dev/nullにパイプします。これにより、既存のプロセスを大幅に書き直さなくても、探していることを正確に実行できます。

「/ usr/bin/time」からのstderrチャタリングなしで同じことをするために別のプログラムを思いつくかもしれないと私は想像します。あるいは自分で書いても、それは単なる 'fork'(またはexec()の派生物)です。

「/ usr/bin/time」を使用することを忘れないでください。bashからの組み込みの「time」が信号の同じ「食べる」ことを行うかどうかはわかりません。

2
lornix