เรียกใช้หลายคำสั่งและฆ่าพวกเขาเป็นหนึ่งในทุบตี


51

ฉันต้องการเรียกใช้หลายคำสั่ง (โปรเซส) ในเชลล์เดียว พวกเขาทั้งหมดมีผลผลิตต่อเนื่องของตัวเองและไม่หยุด เล่นกับพวกเขาในการแบ่งพื้นหลัง-Ctrl Cผมอยากจะเรียกพวกเขาเป็นกระบวนการเดียวที่จะสามารถที่จะหยุดทั้งหมดของพวกเขาด้วย (subshell อาจจะ?) -CtrlC

โดยเฉพาะฉันต้องการเรียกใช้การทดสอบหน่วยด้วยmocha(โหมดดู) เรียกใช้เซิร์ฟเวอร์และเรียกใช้การประมวลผลไฟล์ล่วงหน้า (โหมดดู) และดูผลลัพธ์ของแต่ละรายการในหน้าต่างเทอร์มินัลเดียว โดยทั่วไปฉันต้องการหลีกเลี่ยงการใช้งานนักวิ่ง

ฉันสามารถรับรู้ได้โดยเรียกใช้กระบวนการในพื้นหลัง ( &) แต่จากนั้นฉันต้องใส่พวกเขาลงในเบื้องหน้าเพื่อหยุดพวกเขา ฉันต้องการที่จะมีกระบวนการห่อหุ้มพวกเขาและเมื่อฉันหยุดกระบวนการมันจะหยุด 'ลูก' ของมัน


พวกเขาควรทำงานพร้อมกันหรืออย่างใดอย่างหนึ่งหลังจากที่อื่น?
Minix

ใช่กระบวนการควรทำงานพร้อมกัน แต่ฉันต้องเห็นผลลัพธ์จากแต่ละกระบวนการ
user1876909

คำตอบ:


67

ในการรันคำสั่งพร้อมกันคุณสามารถใช้&ตัวคั่นคำสั่ง

~$ command1 & command2 & command3

สิ่งนี้จะเริ่มcommand1ขึ้นจากนั้นรันในพื้นหลัง command2เช่นเดียวกันกับ จากนั้นก็จะเริ่มcommand3ตามปกติ

ผลลัพธ์ของคำสั่งทั้งหมดจะอ่านไม่ออกด้วยกัน แต่ถ้านั่นไม่ใช่ปัญหาสำหรับคุณนั่นจะเป็นทางออก

หากคุณต้องการแยกเอาท์พุทในภายหลังคุณสามารถไพพ์เอาท์พุทของแต่ละคำสั่งลงไปteeซึ่งช่วยให้คุณระบุไฟล์เพื่อทำมิเรอร์เอาต์พุต

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

ผลลัพธ์อาจจะยุ่งมาก sedเพื่อตอบโต้ที่คุณสามารถให้การส่งออกของทุกคำสั่งที่ใช้คำนำหน้า

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

ดังนั้นถ้าเรารวมทั้งหมดเข้าด้วยกันเราจะได้:

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3

นี่เป็นเวอร์ชั่นที่เหมาะอย่างยิ่งสำหรับสิ่งที่คุณอาจจะได้เห็น แต่มันเป็นสิ่งที่ดีที่สุดที่ฉันสามารถนึกได้ในตอนนี้

หากคุณต้องการหยุดทั้งหมดในครั้งเดียวคุณสามารถใช้บิลด์อินtrapได้

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

ซึ่งจะดำเนินการcommand1และcommand2ในพื้นหลังและcommand3ในเบื้องหน้าซึ่งจะช่วยให้คุณฆ่ามันด้วย+ CtrlC

เมื่อคุณฆ่ากระบวนการสุดท้ายด้วยCtrl+ คำสั่งจะถูกดำเนินการเพราะเราเชื่อมต่อการดำเนินการของพวกเขากับการต้อนรับของสัญญาณรบกวนสิ่งที่ส่งมาโดยการกด+Ckill %1; kill %2CtrlC

พวกเขาตามลำดับฆ่ากระบวนการพื้นหลังที่ 1 และ 2 (ของคุณcommand1และcommand2) trap - SIGINTอย่าลืมที่จะเอากับดักหลังจากที่คุณทำเสร็จแล้วด้วยคำสั่งของคุณโดยใช้

มอนสเตอร์ที่สมบูรณ์ของคำสั่ง:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'

คุณสามารถของหลักสูตรมีลักษณะที่หน้าจอ มันช่วยให้คุณสามารถแยกคอนโซลออกเป็นคอนโซลแยกได้มากเท่าที่คุณต้องการ ดังนั้นคุณสามารถตรวจสอบคำสั่งทั้งหมดแยกกัน แต่ในเวลาเดียวกัน


3
รอการวิจารณ์ทั้งหมด :)
Minix

2
ขอขอบคุณ. มันทำสิ่งที่ฉันต้องการอย่างแน่นอน (คำสั่ง trap) การแก้ปัญหาจะต้องห่อในสคริปต์เพื่อนำมาใช้ใหม่ได้
user1876909

@ user1876909 นี่คือโครงกระดูก ข้อมูลเฉพาะนั้นขึ้นอยู่กับคุณ [1] ดีใจที่ฉันสามารถช่วย [1] pastebin.com/jKhtwF40
Minix

3
นี่เป็นคำตอบที่ค่อนข้างชาญฉลาด iv เรียนรู้สิ่งใหม่ bravo +1
mike-m

1
มันอัศจรรย์มาก. ฉันมีจำนวนมากที่จะขุดและเรียนรู้จากคำตอบนี้
ccnokes

20

คุณสามารถฆ่าพวงของกระบวนการในครั้งเดียวถ้าคุณจัดให้เรียกพวกเขา (และพวกเขา) ในเดียวกันกลุ่มกระบวนการ

Linux จัดเตรียมยูทิลิตี้setsidให้เรียกใช้โปรแกรมในกลุ่มกระบวนการใหม่ (ในเซสชันใหม่แม้ แต่เราไม่สนใจสิ่งนั้น) (สิ่งนี้สามารถทำได้ แต่ซับซ้อนกว่าหากไม่มีsetsid)

ID กลุ่มกระบวนการ (PGID) ของกลุ่มกระบวนการคือ ID กระบวนการของกระบวนการหลักดั้งเดิมในกลุ่ม หากต้องการฆ่ากระบวนการทั้งหมดในกลุ่มกระบวนการให้ส่งค่าลบของ PGID ไปยังการkillเรียกระบบหรือคำสั่ง PGID ยังคงใช้ได้แม้ว่ากระบวนการดั้งเดิมที่มี PID นี้จะตาย (แม้ว่ามันอาจจะสับสนเล็กน้อยก็ตาม)

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"

หากคุณเรียกใช้กระบวนการในพื้นหลังจากเชลล์ที่ไม่มีการโต้ตอบกระบวนการเหล่านั้นจะยังคงอยู่ในกลุ่มกระบวนการของเชลล์ มันมีเฉพาะในเชลล์แบบโต้ตอบที่กระบวนการพื้นหลังทำงานในกลุ่มกระบวนการของตัวเอง ดังนั้นหากคุณแยกคำสั่งจากเชลล์ที่ไม่มีการโต้ตอบซึ่งยังคงอยู่ในพื้นหน้าCtrl+ Cจะฆ่าพวกเขาทั้งหมด ใช้waitbuiltin เพื่อทำให้เชลล์รอให้คำสั่งทั้งหมดออก

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all

2
คำตอบที่ไม่ได้โต้แย้ง (วิธีที่ด้านล่าง) ง่ายต่อการเข้าใจและจดจำ
Nicolas Marshall

14

ฉันประหลาดใจที่นี่ไม่ได้อยู่ที่นี่ แต่วิธีที่ฉันชอบในการทำเช่นนี้คือการใช้subshellsด้วยคำสั่งพื้นหลัง การใช้งาน:

(command1 & command2 & command3)

เอาต์พุตสามารถมองเห็นได้จากทั้งสองคำสั่ง bash และสิ่งอำนวยความสะดวกทั้งหมดยังคงมีอยู่และจะCtrl-cฆ่าได้ทั้งหมด ฉันมักจะใช้สิ่งนี้เพื่อเริ่มเซิร์ฟเวอร์ไฟล์ไคลเอนต์แบบดีบักและเซิร์ฟเวอร์ส่วนหลังในเวลาเดียวกันดังนั้นฉันสามารถฆ่าพวกเขาทั้งสองได้อย่างง่ายดายเมื่อฉันต้องการ

วิธีการทำงานนี้คือcommand1และcommand2กำลังทำงานในพื้นหลังของอินสแตนซ์ของ subshell ใหม่และcommand3อยู่เบื้องหน้าของอินสแตนซ์นั้นและ subshell เป็นสิ่งแรกที่ได้รับสัญญาณ kill จากCtrl-ckeypress มันเหมือนกับการรันสคริปต์ทุบตีด้วยความเคารพว่าใครเป็นเจ้าของกระบวนการใดและเมื่อใดที่โปรแกรมแบ็คกราวน์ถูกฆ่า


หากคุณเพิ่มwaitเป็นคำสั่งสุดท้าย (command3 ในตัวอย่างของคุณ) คุณสามารถรันโค้ดล้างข้อมูลหลังจากผู้ใช้กด Ctrl-c
dotnetCarpenter


-2

pipeคุณสามารถใช้ นี่จะเริ่มคำสั่งที่ปลายทั้งสองของไพพ์ในเวลาเดียวกัน คุณควรจะสามารถหยุดคำสั่งทั้งหมดด้วยCTRL-Cเช่นกัน

1st command | 2nd command | 3rd command

หากคุณต้องการเรียกใช้คำสั่งหนึ่งหลังจากนั้นคุณสามารถใช้วิธีการของ @ serenesat


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