Cara membuat `xargs` mengabaikan pintu keluar anak dan terus memproses lebih lanjut

24

Saya kadang-kadang menjalankan xargspekerjaan panjang dalam semalam dan sangat menjengkelkan untuk menemukan di pagi hari yang xargsmeninggal di suatu tempat di tengah, misalnya karena kesalahan segmentasi dalam satu kasus khusus, seperti yang terjadi malam ini.

Jika bahkan satu xargsanak terbunuh, itu tidak memproses input lagi:

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

Konsol 2:

[09:35:54] kill 5601

Dapatkah saya mencegah xargsberhenti memproses input lagi setelah proses anak meninggal dan melanjutkan pemrosesan?

Christoph Wurm
sumber
Saya menggunakan xargsversi 4.4.2 debian wheezydan sepertinya setiap hal berjalan dengan baik bahkan jika saya membunuh sleepproses tertentu . Versi apa xargsyang Anda gunakan? mungkin mereka telah memperbaiki masalah ini dalam versi terbaru.
Kannan Mohan
Sedikit terlambat ke pesta, tetapi bagaimana xargs ... bash -c '...;exit 0'atau bahkanxargs ... bash -c '... || echo erk'
Samveen
Perhatikan bahwa parallel -j 1ini adalah solusi hack yang mungkin.
barrycarter

Jawaban:

25

Tidak, kamu tidak bisa. Dari xargssumber di 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;

Tidak ada bendera di sekitar pemeriksaan itu, atau di sekitar fungsi yang memanggilnya. Tampaknya memang terkait dengan procs maks, yang saya kira masuk akal: jika Anda mengatur procs maks cukup tinggi, tidak akan repot memeriksa sampai mencapai batas, yang Anda mungkin tidak akan pernah.

Solusi yang lebih baik untuk apa yang Anda coba lakukan mungkin menggunakan GNU Make :

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

all: $(TARGETS)

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

Kemudian:

$ make -k -j4 

akan memiliki efek yang sama, dan memberi Anda kontrol yang jauh lebih baik.

ckhan
sumber
9

Tampaknya salah satu bahasa sehari-hari yang paling jelas hanya disinggung oleh proposal lain.

Artinya, Anda dapat menggunakan yang berikut ini:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

untuk "memaksa kesuksesan".

Catatan, ini sejalan dengan proposal oleh lornix (tidak banyak kata).

Bagaimanapun, karena ini secara efektif mengabaikan status keluar proses yang sebenarnya, saya akan memastikan Anda mempertimbangkan entah bagaimana menyimpan status sub-proses untuk analisis post-mortem. Misalnya:

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

Di truesini agak berlebihan dan jadi ini mungkin lebih baik ditulis sebagai:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Karena kami mungkin ingin tahu kapan file 'gagal' tidak dapat disentuh. Dengan kata lain, kita tidak lagi mengabaikan kegagalan, kita memperhatikan dan melanjutkan.

Dan, setelah mempertimbangkan sifat rekursif dari masalah ini, mungkin kita melihat persis mengapa xargs tidak membuat kegagalan kegagalan menjadi mudah. Karena itu tidak pernah merupakan ide yang baik - Anda harus meningkatkan penanganan kesalahan dalam proses yang Anda kembangkan sebagai gantinya. Saya percaya gagasan ini, bagaimanapun, lebih melekat pada "filsafat Unix" itu sendiri.

Akhirnya, saya kira ini juga yang disinggung oleh James Youngman dengan merekomendasikan trap, yang mungkin dapat digunakan dengan cara yang sama. Artinya, jangan abaikan masalahnya ... jebak dan tangani atau Anda bangun suatu hari dan mendapati bahwa tidak ada sub-program yang berhasil sama sekali ;-)

kingofephyra
sumber
3

Gunakan 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

Atau beralih dari shell ke bahasa lain di mana Anda juga dapat mengatur penangan sinyal.

Perhatikan juga bahwa setelah bash -c foo..Anda harus menentukan nilai yang $0harus diambil (di sini, fnord) sehingga kata pertama yang dihasilkan oleh seqtidak dimakan.

James Youngman
sumber
2

Letakkan perintah lain di sana untuk 'memakan' sinyal dari program sekarat.

Saya mencoba contoh Anda, secara tidak langsung seperti yang ditunjukkan untuk membuktikan masalahnya ... 'killall sleep' membunuh proses tidur, mengganggu bash, dan xargs berhenti.

Sebagai tes, saya terjebak perintah 'jalankan perintah lain' di antara xargs dan bash ... dalam hal ini '/ usr / bin / time'. kali ini (tanpa kata-kata), killall sleep membunuh proses tidur, tetapi xargs terus berlanjut.

Anda akan menyalurkan output waktu ke / dev / null, dan ini akan melakukan apa yang Anda cari tanpa menulis ulang utama dari proses yang ada.

Saya membayangkan jika saya merenung sejenak saya bisa membuat program lain untuk melakukan hal yang sama tanpa obrolan stderr dari '/ usr / bin / time'. Atau bahkan menulis sendiri, itu hanya turunan 'fork' (atau exec ()).

Ingatlah untuk menggunakan '/ usr / bin / time', karena saya tidak yakin 'waktu' bawaan dari bash akan melakukan 'makan' sinyal yang sama.

lornix
sumber
1
Alternatif yang baik timeuntuk tujuan ini adalah env, karena yang dilakukannya hanyalah menambahkan nol atau lebih variabel opsional ke lingkungan program yang dijalankannya. Itu tidak memancarkan output sendiri, dan kode kembali dari program yang dipanggil akan diteruskan kembali ke apa pun yang dipanggil env.
James Sneeringer
{Chuckle} Aku memikirkan itu beberapa saat setelah aku menulis ini. Waktu adalah hal pertama yang terlintas dalam pikiran sebagai perintah "jalankan sesuatu". Bekerja dengan baik. Selamat dan terima kasih.
lornix
2

Baik timeatau envbekerja untuk saya (mereka menyampaikan nilai kembali program anak mereka) jadi saya menulis bliss:

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

kemudian chmod u+x ~/bliss

dan sesuatu seperti find_or_similar | xargs ~/bliss fatally_dying_program.sh

pix
sumber