จะไม่ทำสิ่งใดตลอดไปอย่างสง่างาม?


82

ผมมีโปรแกรมที่ก่อให้ข้อมูลที่เป็นประโยชน์ในแต่ยังอ่านจากstdout stdinฉันต้องการเปลี่ยนเส้นทางเอาต์พุตมาตรฐานไปยังไฟล์โดยไม่ต้องให้อะไรกับอินพุตมาตรฐาน จนถึงตอนนี้ดีมาก: ฉันทำได้:

program > output

และไม่ทำอะไรเลยใน tty

อย่างไรก็ตามปัญหาคือฉันต้องการทำในพื้นหลัง ถ้าฉันทำ:

program > output &

โปรแกรมจะถูกระงับ ("ถูกระงับ (อินพุต tty)")

ถ้าฉันทำ:

program < /dev/null > output &

โปรแกรมหยุดทำงานทันทีเนื่องจากถึง EOF

ดูเหมือนว่าสิ่งที่ฉันต้องการก็คือการเข้าไปในท่อสิ่งที่ไม่ได้ทำอะไรสำหรับจำนวนเงินที่แน่นอนของเวลาและไม่ได้อ่านprogram stdinแนวทางต่อไปนี้ใช้ได้ผล:

while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &

อย่างไรก็ตามทั้งหมดนี้น่าเกลียดมาก มีมีจะเป็นวิธีที่สง่างามใช้สาธารณูปโภค Unix มาตรฐานเพื่อ "ทำอะไรไปเรื่อย ๆ" (ถอดความman true) ฉันจะบรรลุสิ่งนี้ได้อย่างไร (เกณฑ์หลักของฉันสำหรับความสง่างามที่นี่: ไม่มีไฟล์ชั่วคราวไม่มีการรอไม่ว่างหรือการปลุกเป็นระยะ ๆ ไม่มีสาธารณูปโภคแปลกใหม่สั้นที่สุดเท่าที่จะทำได้)


ลองsu -c 'program | output &' userดู ฉันกำลังจะถามคำถามที่คล้ายกันกับการสร้างงานพื้นหลังเป็นวิธีที่ยอมรับได้สำหรับการจัดการ "บริการ / ภูต" ฉันยังพบว่าฉันไม่สามารถเปลี่ยนเส้นทางโดยไม่ได้เปลี่ยนเส้นทางSTDERR STDOUTวิธีแก้ปัญหาที่ programA ส่งSTDOUTไปยังSTDINของ programB จากนั้นเปลี่ยนเส้นทางSTDERRไปยังล็อกไฟล์:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc

อาจจะ ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc

โปรแกรมประเภทใด
João Portela

João Portela: นี่คือโปรแกรมที่ฉันเขียน, gitorious.org/irctk
a3nm

ทำไมไม่เพียงเพิ่มสวิตช์ไปยังโปรแกรมที่คุณเขียน นอกจากนี้ฉันคิดว่าถ้าคุณปิด stdin ด้วย1<&-มันจะออกจากโปรแกรมของคุณ?
w00t

คำตอบ:


16

ในเปลือกหอยที่สนับสนุนพวกเขา (ksh, zsh, bash4), คุณสามารถเริ่มต้นการprogramเป็นผู้ร่วมกระบวนการ

  • ksh: program > output |&
  • zsh, bash:coproc program > output

ที่เริ่มต้นในพื้นหลังด้วยการป้อนข้อมูลของการเปลี่ยนเส้นทางจากprogram pipeปลายอีกด้านหนึ่งของท่อเปิดให้เปลือก

ประโยชน์สามประการของวิธีการนั้น

  • ไม่มีกระบวนการพิเศษ
  • คุณสามารถออกจากสคริปต์เมื่อprogramตาย (ใช้waitเพื่อรอ)
  • programจะยุติ (รับeofstdin ถ้าเชลล์ออก)

ดูเหมือนว่าจะทำงานและดูเหมือนเป็นความคิดที่ยอดเยี่ยม! (เพื่อความเป็นธรรมฉันได้ขอบางสิ่งบางอย่างที่จะไปตามคำสั่งของฉันไม่ใช่สำหรับคุณสมบัติเชลล์ แต่นี่เป็นเพียงปัญหา XY ที่เล่น) ฉันกำลังพิจารณาที่จะยอมรับคำตอบนี้แทนที่จะเป็นคำตอบของ @ PT
a3nm

1
@ a3nm tail -f /dev/nullไม่เหมาะอย่างยิ่งที่จะอ่านทุก ๆ วินาที/dev/null(หาง GNU รุ่นปัจจุบันบน Linux ที่ใช้การรู้แจ้งจริงมีข้อผิดพลาด ) sleep infหรือความสามารถในการพกพาที่เทียบเท่ากันsleep 2147483647นั้นเป็นวิธีการที่ดีกว่าสำหรับคำสั่งที่ไม่มี IMO อยู่ (โน้ตที่sleepสร้างขึ้นในเชลล์ไม่กี่ตัวเช่นksh93หรือmksh)
Stéphane Chazelas

78

ฉันไม่คิดว่าคุณจะสง่างามไปกว่านี้อีกแล้ว

tail -f /dev/null

ที่คุณแนะนำไว้แล้ว (สมมติว่าการใช้สิ่งนี้แจ้งภายในไม่ควรมีการสำรวจหรือปลุกดังนั้นนอกเหนือจากการมองที่แปลกมันควรจะเพียงพอ)

คุณต้องการโปรแกรมอรรถประโยชน์ที่จะทำงานไปเรื่อย ๆ จะเปิด stdout ไว้ แต่จะไม่เขียนอะไรไปยัง stdout และจะไม่ออกเมื่อ stdin ถูกปิด สิ่งที่ต้องการyesเขียนถึง stdout จริง ๆ catจะออกเมื่อ stdin ถูกปิด (หรืออะไรก็ตามที่คุณสั่งให้ทำ) ฉันคิดว่าsleep 1000000000dอาจใช้งานได้ แต่สิ่งที่tailดีกว่าชัดเจน กล่อง Debian ของฉันมีtailfคำสั่งที่สั้นลงเล็กน้อย

การตะปูที่แตกต่างกันเกี่ยวกับวิธีการใช้โปรแกรมภายใต้screen?


ฉันชอบtail -f /dev/nullวิธีการที่ดีที่สุดและพบว่ามันหรูหราพอเช่นกันเนื่องจากการใช้คำสั่งตรงกับวัตถุประสงค์ที่ต้องการอย่างใกล้ชิด
jw013

2
จากstrace tail -f /dev/nullดูเหมือนว่าtailใช้inotifyและ wakeups sudo touch /dev/nullเกิดขึ้นในกรณีที่โง่เหมือน มันน่าเศร้าที่ดูเหมือนว่าจะไม่มีทางออกที่ดีกว่า ... ฉันสงสัยว่า syscall ที่ถูกต้องที่จะใช้ในการใช้โซลูชันที่ดีกว่า
a3nm

5
@ a3nm syscall จะเป็นpauseแต่มันไม่ได้สัมผัสโดยตรงกับส่วนต่อประสานของเชลล์
Gilles

PT: ฉันรู้เกี่ยวกับscreenแต่นี่คือการเรียกใช้โปรแกรมที่เกิดขึ้นหลายครั้งจากเชลล์สคริปต์เพื่อวัตถุประสงค์ในการทดสอบดังนั้นการใช้จึงscreenเป็นเรื่องเล็กน้อย
a3nm

3
@sillyMunky Silly Monkey คำตอบของ WaelJ นั้นผิด (ส่งศูนย์ที่ไม่มีที่สิ้นสุดไปยัง stdin)
PT

48

sleep infinity เป็นทางออกที่ชัดเจนที่สุดที่ฉันรู้

คุณสามารถใช้infinityเพราะsleepรับจำนวนจุดลอย*ซึ่งอาจจะเป็นทศนิยม , เลขฐานสิบหก , อินฟินิตี้หรือน่านman strtodตาม

* นี่ไม่ใช่ส่วนหนึ่งของมาตรฐาน POSIX ดังนั้นจึงไม่สามารถพกพาtail -f /dev/nullได้ อย่างไรก็ตามรองรับใน GNU coreutils (Linux) และ BSD (ใช้กับ Mac) (เห็นได้ชัดว่าไม่รองรับ Mac รุ่นใหม่กว่า - ดูความคิดเห็น)


ฮ่าฮ่าเป็นวิธีที่ดีจริงๆ :)
a3nm

@ a3nm: ขอบคุณ:) ดูเหมือนว่าsleep infinityยังทำงานบน BSD และ Mac
Zaz

ทรัพยากรประเภทใดที่กระบวนการนอนหลับไม่สิ้นสุดใช้เวลา? แค่แรม?
CMCDragonkai

1
คำตอบนี้อ้างว่าsleep infinityรอได้สูงสุด 24 วัน ใครถูก
nh2

1
@Zaz ฉันได้ตรวจสอบปัญหาโดยละเอียดแล้ว ปรากฎว่าคุณถูกต้องในตอนแรก! sleepกระบะไม่ จำกัด 24 วัน ; มันเป็นตึกระฟ้าแห่งแรกที่หลับเป็นเวลา 24 วันและหลังจากนั้นมันจะทำตึกระฟ้าอีกมากมาย ดูความคิดเห็นของฉันที่นี่: stackoverflow.com/questions/2935183/…
nh2

19
sleep 2147483647 | program > output &

ใช่2^31-1เป็นจำนวน จำกัด และมันจะไม่ทำงานตลอดไปแต่ฉันจะให้คุณ $ 1,000 เมื่อการนอนหลับหมดลงในที่สุด (คำแนะนำ: หนึ่งในนั้นจะต้องตายในเวลานั้น)

  • ไม่มีไฟล์ชั่วคราว ตรวจสอบ
  • ไม่มีการรอคอยที่ยุ่งหรือปลุกเป็นระยะ ตรวจสอบ
  • ไม่มีสาธารณูปโภคที่แปลกใหม่ ตรวจสอบ
  • สั้นที่สุด โอเคมันอาจจะสั้นกว่านี้

5
bash: sleep $ ((64 # 1 _____)) | โปรแกรม> ผลลัพธ์ &
Diego Torres Milano

นั่นนอนเป็นเวลา 68 ปีนี่หลับไป 98 ศตวรรษ : sleep 2147483647d...
agc

9

คุณสามารถสร้างไบนารีที่ทำได้ด้วย:

$ echo 'int main(){ pause(); }' > pause.c; make pause

5

นี่คือข้อเสนอแนะอื่นใช้สาธารณูปโภค Unix มาตรฐานเพื่อ "ทำอะไรไปเรื่อย ๆ"

sh -c 'kill -STOP $$' | program > output

สิ่งนี้จะทำให้เชลล์ที่ถูกส่งไปในทันทีSIGSTOPซึ่งหยุดกระบวนการ ใช้เป็น "อินพุต" ในโปรแกรมของคุณ ส่วนประกอบของSIGSTOPคือSIGCONTถ้าคุณรู้ว่าเปลือกมี PID 12345 คุณสามารถที่kill -CONT 12345จะทำให้มันดำเนินต่อไป


2

บน Linux คุณสามารถ:

read x < /dev/fd/1 | program > output

บน Linux การเปิด / dev / fd / x โดยที่ x เป็นตัวอธิบายไฟล์ไปยังจุดสิ้นสุดการเขียนของไพพ์ทำให้คุณได้รับจุดสิ้นสุดการอ่านของไพพ์ดังนั้นที่นี่จะเหมือนกับใน stdin ของโปรแกรม โดยพื้นฐานแล้วreadจะไม่มีวันกลับคืนเพราะสิ่งเดียวที่อาจเขียนลงไปในท่อนั้นคือตัวของมันเองและreadไม่ส่งออกอะไรเลย

มันจะทำงานบน FreeBSD หรือ Solaris แต่ด้วยเหตุผลอื่น ที่นั่นการเปิด / dev / fd / 1 ทำให้คุณได้รับทรัพยากรเช่นเดียวกับ open บน fd 1 ตามที่คุณคาดหวังและเป็นระบบส่วนใหญ่ยกเว้น Linux ทำดังนั้นจุดสิ้นสุดการเขียนของไพพ์ อย่างไรก็ตามบน FreeBSD และ Solaris ท่อเป็นแบบสองทิศทาง ดังนั้นตราบใดที่programไม่ได้เขียนไปยัง stdin (ไม่มีแอปพลิเคชัน) readจะไม่ได้รับการอ่านจากทิศทางของไปป์นั้น

บนระบบที่ไพพ์ไม่ใช่แบบสองทิศทางreadอาจล้มเหลวด้วยข้อผิดพลาดเมื่อพยายามอ่านจากไฟล์ descriptor แบบเขียนอย่างเดียว /dev/fd/xนอกจากนี้ทราบว่าระบบทั้งหมดไม่ได้


ดีมาก! ในความเป็นจริงการทดสอบของฉันคุณไม่ต้องการxด้วยการทุบตี; ต่อไปกับ zsh คุณสามารถทำได้readและใช้งานได้ (แม้ว่าฉันไม่เข้าใจว่าทำไม!) นี่เป็นเคล็ดลับเฉพาะสำหรับ Linux หรือใช้กับระบบ * nix ทั้งหมดหรือไม่
a3nm

@ a3nm ถ้าคุณทำreadคนเดียวมันจะอ่านจาก stdin ดังนั้นหากเป็นเทอร์มินัลเครื่องจะอ่านสิ่งที่คุณพิมพ์จนกว่าคุณจะกด Enter
Stéphane Chazelas

แน่นอนฉันเข้าใจสิ่งที่อ่าน สิ่งที่ฉันไม่เข้าใจคือสาเหตุที่การอ่านจากเทอร์มินัลด้วยการอ่านในกระบวนการที่อยู่เบื้องหลังกำลังบล็อกด้วยการทุบตี แต่ไม่ใช่กับ zsh
a3nm

@ a3nm ฉันไม่แน่ใจว่าคุณหมายถึงอะไร คุณหมายถึงอะไรโดยที่คุณสามารถทำได้readและใช้งานได้ ?
Stéphane Chazelas

ฉันกำลังบอกว่าด้วย zsh คุณสามารถทำได้read | program > outputและมันทำงานในลักษณะเดียวกับที่คุณแนะนำ (และฉันก็ไม่เข้าใจว่าทำไม)
a3nm

0

Stéphane Chazelas' read วิธีการแก้ปัญหาการทำงานบน Mac OS X เช่นกันถ้า FD /dev/fd/1อ่านได้รับการเปิดใน

# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  1 0

exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  0 0

เพื่อให้สามารถที่จะฆ่าtail -f /dev/nullในสคริปต์ (มี SIGINT ตัวอย่างเช่น) มีความจำเป็นต้องพื้นหลังและสั่งการtailwait

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!

-2

เปลี่ยนเส้นทาง/dev/zeroเป็นอินพุตมาตรฐาน!

program < /dev/zero > output &

9
สิ่งนี้จะทำให้โปรแกรมของเขามีจำนวนศูนย์เป็นไบต์ไม่สิ้นสุด ... ซึ่งน่าเศร้าที่มันทำให้มันยุ่งวน
Jander

1
นี่ไม่ใช่เรื่องอื้อฉาวที่แท้จริง / dev / ศูนย์จะไม่ปิดถือโซ่เปิดอยู่ อย่างไรก็ตามผู้โพสต์บอกว่าเขาไม่ได้เข้าร่วมโปรแกรมดังนั้นจะไม่มีศูนย์ใดถูกถ่ายโอนไปยังรายการ นี่ไม่ใช่การวนรอบที่ยุ่งเลยมันเป็นการรอที่บริสุทธิ์
sillyMunky

2
ขออภัย OP ใช้ stdin ดังนั้นสิ่งนี้จะล้างอินพุตของเขาและจะวาดจาก / dev / ศูนย์ ฉันควรอ่านสองครั้งในครั้งต่อไป! ถ้า OP ไม่ได้ใช้ stdin นี่จะเป็นทางออกที่ยอดเยี่ยมที่สุดที่ฉันเคยเห็นและจะไม่รอช้า
sillyMunky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.