วิธีทำให้ `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คุณใช้เวอร์ชั่นไหน อาจเป็นเพราะพวกเขาได้แก้ไขปัญหาในรุ่นล่าสุดแล้ว
Kannan Mohan

สายไปงานเลี้ยงเล็ก ๆ น้อย ๆ แต่จะเกี่ยวกับxargs ... bash -c '...;exit 0'หรือแม้กระทั่งxargs ... bash -c '... || echo erk'
Samveen

โปรดทราบว่า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 procs ซึ่งฉันคิดว่าสมเหตุสมผล: ถ้าคุณตั้ง 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)'

เพื่อ "บังคับให้สำเร็จ"

โปรดทราบว่านี่เป็นไปตามบรรทัดของข้อเสนอโดย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' ฆ่ากระบวนการนอนหลับขัดจังหวะการทุบตีและจบการทำงาน xargs

จากการทดสอบฉันติดคำสั่ง 'เรียกใช้คำสั่งอื่น' ในระหว่าง xargs และ bash ... ในกรณีนี้ '/ usr / bin / time' ครั้งนี้ (ไม่มีเล่นสำนวน) killall sleep ฆ่ากระบวนการ sleep แต่ xargs ยังคงดำเนินต่อไป

คุณจะส่งออกเวลาของ / dev / null และสิ่งนี้จะทำสิ่งที่คุณกำลังมองหาโดยไม่ต้องเขียนกระบวนการที่มีอยู่ของคุณ

ฉันคิดว่าถ้าฉันไตร่ตรองสักครู่ฉันสามารถหาโปรแกรมอื่นมาทำเช่นเดียวกันได้โดยไม่พูดถึง stderr จาก '/ usr / bin / time' หรือแม้แต่เขียนหนึ่งตัวเองมันเป็นแค่อนุพันธ์ 'fork' (หรือ exec ())

จำไว้ว่าให้ใช้ '/ usr / bin / time' เพราะฉันไม่แน่ใจว่า 'เวลา' ในตัวจากการทุบตีจะทำเช่นเดียวกันกับ 'การกิน' ของสัญญาณ


1
ทางเลือกที่ดีtimeสำหรับจุดประสงค์นี้คือenvเนื่องจากมันเพิ่มตัวแปรที่เป็นศูนย์หรือมากกว่านั้นเข้ากับสภาพแวดล้อมของโปรแกรมที่รัน envมันส่งเสียงเอาท์พุทของตัวเองไม่ได้และรหัสการกลับมาของโปรแกรมเรียกจะถูกส่งกลับไปที่สิ่งที่เรียกว่า
James Sneeringer

{Chuckle} ฉันคิดว่าหลังจากนั้นไม่นานฉันก็เขียนมันขึ้นมา เวลาเป็นสิ่งแรกที่นึกได้ว่าเป็นคำสั่ง "รันบางสิ่ง" ทำงานได้ดีแม้ว่า ขอแสดงความยินดีและขอบคุณ
lornix

2

ทั้งtimeมิได้envทำงานให้ฉัน (พวกเขาผ่านพร้อมค่าตอบแทนของโปรแกรมเด็กของพวกเขา) ดังนั้นฉันเขียนbliss:

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

แล้วก็ chmod u+x ~/bliss

และสิ่งที่ชอบ find_or_similar | xargs ~/bliss fatally_dying_program.sh

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.