ในบางเวลาจากนี้ทำบางสิ่งบางอย่าง (และอาจแสดงผลลัพธ์ในคอนโซล)


12

ฉันใช้เซิร์ฟเวอร์ Ubuntu 16.04 และฉันต้องการที่จะใช้ยูทิลิตี้atในเซสชันปัจจุบันของฉันเพื่อทำบางสิ่งบางอย่าง 1 นาทีนับจากนี้ (พูด, echo) โดยไม่ต้องระบุวันที่และเวลา - เพียง 1 นาทีล่วงหน้าจากเวลาปัจจุบัน

สิ่งนี้ล้มเหลว:

echo 'hi' | at 1m

เหตุผลที่ฉันเลือกatมากกว่าsleepเพราะช่วงพักการนอนหลับเป็นช่วงปัจจุบันและเหมาะสำหรับการชะลอคำสั่งในเซสชันอื่นมากกว่าที่เราทำงานด้วยเวลาส่วนใหญ่ AFAIR atไม่ทำงานในลักษณะนี้และจะไม่ทำให้เสียเวลาของฉัน

Update_1

โดยคำตอบของ Pied Piper ฉันได้ลอง:

(sleep 1m; echo 'hi')  &

ฉันมีปัญหากับวิธีนี้: กระแสข้อมูล "สวัสดี" ถูกพิมพ์ภายในพรอมต์หลักของฉันและเพิ่มพรอมต์รองว่างเปล่า ( _) ทันทีภายใต้พรอมต์หลักที่มีให้ดู:

USER@:~# (sleep 1m; echo 'hi')  &
[1] 22731
USER@:~# hi
^C
[1]+  Done

Update_2

โดยคำตอบของ Peter Corde ฉันลอง:

(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)

สิ่งนี้ทำงานได้อย่างถูกต้องใน Bash 4.4 แต่ดูเหมือนจะไม่ใช่ในเวอร์ชั่นที่เก่ากว่าบางรุ่น (ดูความคิดเห็นในคำตอบ) ฉันใช้ Bash 4.3 เป็นการส่วนตัวในสภาพแวดล้อมของฉันเอง


2
ฉันหวังว่าธงการฆ่าเป็นรูปแบบของ GNU และสามารถจัดเรียงใหม่ได้ดังนั้นจึงไม่มีเสียงเหมือน "kill the wench"!
นิวแฮมป์เชียร์

คำตอบ:


6

สตรีม "hi" ถูกพิมพ์ในพรอมต์ของฉัน (ในstdin) และไม่ใช่ในของฉันstdout

ไม่ไม่ใช่ "พิมพ์ในstdin" ของคุณ

หากคุณกดย้อนกลับจะไม่พยายามเรียกใช้hiเป็นคำสั่ง เป็นเพียงพื้นหลังที่echoพิมพ์ออกมาhiขณะที่เคอร์เซอร์อยู่หลังพรอมต์ของคุณ


บางทีคุณอาจต้องการที่จะพิมพ์บรรทัดใหม่ชั้นนำ(sleep 1m && echo -e '\nhi' &)เช่น (ปรับตามความจำเป็นสำหรับechoในเชลล์ของคุณหรือใช้printfเพื่อการพกพา)

ฉันใส่&ภายใน subshell ที่จะ "ปฏิเสธ" งานพื้นหลังเพื่อให้คุณหลีกเลี่ยง "เสียง" [1]+ Doneของ ด้วย&&ระหว่างคำสั่งให้&ใช้กับห่วงโซ่คำสั่งผสมทั้งหมดดังนั้นจึงกลับไปที่เชลล์พรอมต์ทันทีแทนที่จะรอsleepออกเหมือนที่คุณจะได้รับ(sleep 1; echo ... &)

นี่คือสิ่งที่เกิดขึ้น (ด้วยความล่าช้า 1 วินาที):

peter@volta:~$ (sleep 1 && echo -e '\nhi' &)
peter@volta:~$ 
hi
       # cursor is at the start of this empty line.

Bash ไม่ทราบว่ามีechoบางสิ่งที่พิมพ์ออกมาดังนั้นจึงไม่จำเป็นต้องพิมพ์พรอมต์ใหม่หรือล้างหน้าจอ control-Lคุณสามารถทำได้ด้วยตนเอง

คุณสามารถเขียนฟังก์ชั่นเชลล์ที่ได้รับทุบตีเรื่องการพิมพ์ของพรอมต์หลังจากที่echoเสร็จสิ้น การทำเช่นecho "$PS1"นั้นจะไม่ทำซ้ำอักขระที่พิมพ์ไปแล้ว kill -WINCH $$ในระบบของฉันได้รับทุบตีเพื่อพิมพ์บรรทัดของพรอมต์ใหม่โดยไม่ต้องล้างหน้าจอออกจากhiบรรทัดด้วยตัวเองก่อนที่จะแจ้งให้ SIGWINCH จะถูกส่งโดยอัตโนมัติเมื่อการเปลี่ยนแปลงขนาด WINdow และการตอบสนองของ bash นั้นเกิดขึ้นเพื่อทำสิ่งที่เราต้องการ

มันอาจใช้งานได้กับหอยอื่น ๆ แต่ฉันไม่ได้พยายามที่จะทำให้พกพา / POSIX ได้ 100% ( น่าเสียดายนี่ใช้งานได้กับ bash4.4 เท่านั้นไม่ใช่ bash4.3 หรือขึ้นอยู่กับการตั้งค่าที่ฉันไม่ทราบ )

  # bash4.4
peter@volta:~$ (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
peter@volta:~$ ljlksjksajflasdf dfas      # typed in 2 seconds
hi
peter@volta:~$ ljlksjksajflasdf dfas      # reprinted by Bash because of `kill -WINCH`

คุณสามารถสรุปได้อย่างง่ายดายในฟังก์ชั่นเชลล์ที่ใช้ arg arg sleep และข้อความ

bash 4.3 จะไม่พิมพ์พรอมต์ใหม่หลังจาก SIGWINCH ดังนั้นจึงไม่สามารถใช้งานได้ ฉันshopt -s checkwinsizeเปิดใช้งานทั้งสองระบบแล้ว แต่ใช้ได้กับระบบ Bash 4.4 (Arch Linux) เท่านั้น

หากไม่ได้ผลคุณสามารถลองใช้(sleep 2 && echo -e '\nhi' && echo "$PS1" &)งานว่า "ทำงาน" ใดหากบรรทัดคำสั่งว่างเปล่า (นอกจากนี้ยังละเว้นความเป็นไปได้ที่PROMPT_COMMANDตั้งไว้)


ไม่มีวิธีที่สะอาดจริงๆในการดึงเอาท์พุทของเทอร์มินัลมาผสมกับสิ่งที่คุณอาจทำบนเทอร์มินัลในภายหลังเมื่อตัวจับเวลาเริ่มทำงาน หากคุณอยู่ตรงกลางของการเลื่อนบางอย่างlessคุณอาจพลาดได้ง่าย

หากคุณกำลังมองหาบางสิ่งที่มีผลลัพธ์เชิงโต้ตอบฉันขอแนะนำให้เล่นไฟล์เสียง เช่นกับผู้เล่นบรรทัดคำสั่งเช่นmpv(nice fork ของ MPlayer2) หรือเลือกไฟล์วิดีโอเพื่อให้หน้าต่างใหม่เปิดขึ้น

(sleep 1 && mpv /f/share/music/.../foo.ogg </dev/null &>/dev/null &)

คุณอาจต้องการechoเปิด xterm / konsole / gnome-terminal ใหม่ของคุณ ใช้ตัวเลือกที่ช่วยให้เทอร์มินัลเปิดหลังจากคำสั่งออก


ฉันขอบคุณเดวิดอย่างสุดซึ้งและฉันก็ยกนิ้วให้ นี่คือสิ่งที่ใกล้เคียงที่สุดที่ฉันได้รับเกี่ยวกับสิ่งที่ฉันค้นหา มีวิธีการอัพเกรดคำสั่งต่อไปนี้เพื่อเลียนแบบEnterเหตุการณ์กดปุ่มหลังจากเสียงสะท้อนปรากฏขึ้นดังนั้นฉันจะกลับเข้าสู่หน้าจอหลักของคอนโซลโดยอัตโนมัติหรือไม่ . (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
Arcticooling

@Arcticooling: kill -WINCH $$ ไม่รีเฟรชพรอมต์ของเปลือกทำงานในสถานี นั่นคือจุดทั้งหมดของการใช้งาน การกด Enter จะส่งคำสั่งที่พิมพ์เพียงบางส่วน แต่ WINCH ไม่ได้ดังที่ฉันแสดง หากคุณไม่ได้พิมพ์อะไรเลยแสดงว่าคุณได้รับพรอมต์ที่ว่างเปล่า
Peter Cordes

1
หากคุณเริ่มพิมพ์rm -rf ~/some/directoryให้ป้อนเวลาผิดอาจทำงานrm -rf ~/ได้ (เคล็ดลับความปลอดภัย: พิมพ์เส้นทางให้เสร็จสิ้นจากนั้นควบคุม -a และเปลี่ยนlsเป็นrm -rfดังนั้นนิ้วอ้วนป้อนได้ตลอดเวลาจะไม่ทำลายวันของคุณ)
Peter Cordes

มีสายฉันดำเนินการ(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)และหลังจากเสียงสะท้อนปรากฏขึ้นฉันได้รับพรอมต์ว่างเปล่าหลังจากกดปุ่มEnterฉันได้กลับไปที่พรอมต์หลัก ... ขออภัยถ้าฉันเข้าใจผิดคุณฉันค่อนข้างใหม่กับทุบตี
Arcticooling

@Arcticooling: ฉันใช้ Bash 4.4 บน Arch Linux ในโปรแกรมจำลองเทอร์มินัล Konsole ฉันหวังว่าคำสั่งของฉันจะเป็นแบบพกพาอย่างน้อยกับ bash รุ่นอื่น ๆ แต่อาจไม่ใช่ :( ใช่เพิ่งทดสอบใน Bash 4.3 ภายในscreenเซสชัน (และผ่าน SSH) บนระบบ Ubuntu รุ่นเก่าและฉันมีพฤติกรรมที่คุณอธิบาย
Peter Cordes

13

ที่ถูกต้องในการใช้งานคือat <timespec>ที่ไหน<timespec>เป็นสเปคเวลา คุณยังสามารถใช้-fตัวเลือกเพื่อระบุไฟล์ที่มีคำสั่งเพื่อดำเนินการ หาก-fไม่มีการใช้การเปลี่ยนเส้นทางที่จะอ่านคำสั่งเพื่อดำเนินการจาก stdin (อินพุตมาตรฐาน)

ในตัวอย่างของการดำเนินการคำสั่ง "บาง" (ผมเลือกคำสั่ง "บางคน" จะเป็น "ก้องสวัสดี" ในตัวอย่างด้านล่าง) หนึ่งนาทีหลังจากที่คุณกด Enter (ตอนนี้) ที่ตั้งใจจะใช้เป็น<timespec> now + 1 minuteดังนั้นเพื่อให้ได้ผลลัพธ์ที่คุณต้องการด้วยวิธีแก้ปัญหาแบบหนึ่งซับกรุณาใช้คำสั่งด้านล่าง:

echo 'echo hi' | at now + 1 minute

นี่ควรจะเป็น (และจะทำ) เพื่อเรียกใช้echo hiคำสั่งหลังจากหนึ่งนาที ปัญหาที่นี่คือecho hiคำสั่งจะถูกดำเนินการโดยคำสั่ง at ใน dummy tty และผลลัพธ์จะถูกส่งไปยังกล่องจดหมายของผู้ใช้ (หากมีการกำหนดค่าอย่างถูกต้อง - ซึ่งต้องการคำอธิบายเพิ่มเติมเกี่ยวกับวิธีกำหนดค่ากล่องจดหมายใน เครื่องยูนิกซ์และไม่ได้เป็นส่วนหนึ่งของขอบเขตของคำถามนี้) บรรทัดล่างคือคุณจะไม่สามารถเห็นผลลัพธ์โดยตรงecho hiในเทอร์มินัลเดียวกับที่คุณออกคำสั่ง ทางเลือกที่ง่ายที่สุดคือคุณเปลี่ยนเส้นทางไปยัง "ไฟล์บางไฟล์" ตัวอย่างเช่น:

echo 'echo hi > /tmp/at.out' | at now + 1 minute

ในตัวอย่างข้างต้น "ไฟล์บางไฟล์" คือ/tmp/at.outและผลลัพธ์ที่คาดหวังคือไฟล์at.outภายใต้ / tmp ถูกสร้างขึ้นหลังจากหนึ่งนาทีและต่อท้ายข้อความ 'hi'

นอกเหนือจากnow + 1 minuteตัวอย่างข้างต้นคุณสามารถใช้ข้อกำหนดเวลาอื่น ๆ ได้หลายอย่างแม้กระทั่งบางตัวที่ซับซ้อน คำสั่ง at ฉลาดพอที่จะตีความสิ่งที่มนุษย์อ่านได้เหมือนตัวอย่างด้านล่าง:

now + 1 day, 13:25, mid‐night, noon...

โปรดดูรายละเอียดเพิ่มเติมเกี่ยวกับข้อมูลจำเพาะเวลาที่เป็นไปได้ที่หน้า man at เนื่องจากรูปแบบที่เป็นไปได้นั้นค่อนข้างกว้างขวางและอาจรวมถึงวันที่ญาติหรือเวลาสัมบูรณ์เป็นต้น


2
atโดยค่าเริ่มต้นส่งออกทางไปรษณีย์ แต่สิ่งนี้จะต้องมีการกำหนดค่าอย่างถูกต้องในการทำงาน
Simon Richter

ตอนนี้ฉันมีเงินรางวัล 100 ค่าตอบแทนสำหรับคำตอบ โปรดอ่านข้อความความโปรดปรานของฉันด้านล่างและแก้ไขเพื่ออธิบายเพิ่มเติมเกี่ยวกับสิ่งที่เป็นat <timespec>และการตั้งค่าสถานะที่คุณใช้และหากเหมาะสมกับ Ubuntu 16.04 xenial
Arcticooling

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

7

เรียกใช้sleepในเชลล์ย่อย:

(sleep 60; echo -e '\nhi')  &

หมายเหตุ: บน Linux คุณสามารถกำหนดให้อาร์กิวเมนต์หลับเป็นวินาทีหรือต่อท้ายด้วย s / m / h / d (สำหรับวินาที, นาที, ชั่วโมง, ชั่วโมง, วัน) บน BSD อาร์กิวเมนต์ต้องอยู่ในไม่กี่วินาที


ฉันมีปัญหาเล็กน้อยเกี่ยวกับวิธีการนี้ --- สตรีม "hi" ถูกพิมพ์ลงในพรอมต์ของฉัน (ในstdin) และไม่ได้อยู่ในstdout(ตามปกติของฉันecho)
Arcticooling

1
หากคุณไม่ต้องการให้เอาท์พุทในเทอร์มินัลของคุณปะปนกับพรอมต์และเอาท์พุทของคำสั่งอื่นคุณจะต้องเปลี่ยนมันที่อื่น
PiedPiper

การเปลี่ยนเส้นทางนี้ล้มเหลว (sleep 10s; echo 'hi') & 1>@PiedPiper ตอนนี้ฉันอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้
Arcticooling

2
อ่านเอกสารนี้เป็นความคิดที่ดี :)
PiedPiper

1
@Arcticooling ดูเหมือนว่าคุณกำลังสับสนเกี่ยวกับความหมายของและstdin stdoutพวกเขาไม่มีส่วนเกี่ยวข้องกับสิ่งที่ปรากฏบนสถานีของคุณ คุณควรตีความพวกเขาด้วยความเคารพต่อกระบวนการเฉพาะ(โดยทั่วไปคือโปรแกรม) อินพุตมาตรฐาน ("stdin") สำหรับกระบวนการเป็นแหล่งซึ่งกระบวนการสามารถอ่านข้อมูลและเอาต์พุตมาตรฐานของกระบวนการ ("stdout") เป็นปลายทางที่สามารถเขียนข้อมูลได้ แหล่งที่มาและปลายทางเหล่านี้อาจเป็นโปรแกรมอื่น ๆ หรือไฟล์หรือเทอร์มินัลเอง (สิ่งที่คุณพิมพ์ไปที่ stdin และสิ่งที่มากับ stdout จะปรากฏขึ้น)
David Z

3

ที่ล้มเหลวเพราะคุณกำลังท่อส่งออกของecho "hi"ซึ่งเป็นเพียงhiการatแต่atคาดว่ามันของการป้อนข้อมูลสามารถดำเนินการโดยเปลือกเริ่มต้นระบบ (มักจะทุบตี แต่บางครั้งบางสิ่งบางอย่างที่แตกต่างกันขึ้นอยู่กับระบบ)

นี้:

at 1m << EOF
echo "hi"
EOF

จะบรรลุสิ่งที่คุณพยายามจะทำ มันใช้โครงสร้างเชลล์ที่เรียกว่า 'here document' ซึ่งโดยทั่วไปเป็นวิธีที่ยอมรับได้ในการทำสิ่งนี้ (เพราะมันจัดการหลายบรรทัดได้ดีกว่าการใช้echoคำสั่งและไม่ต้องการสร้างไฟล์ใหม่เหมือนที่คุณต้องการcat) และแปลให้เทียบเท่าที่ค่อนข้างซับซ้อนโดยใช้echo:

echo 'echo "hi"' | at 1m

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


syntax error. Last token seen: m Garbled time ด้วยเหตุผลบางอย่างเช่นทั้งคุณให้ให้ฉันข้อผิดพลาด ฉันมีความคิดว่าทำไมไม่มี. ฉันใช้ Ubuntu 16.04, Bash 4.0 บางทีคุณทำอย่างนั้นในระบบอื่น แม้ว่าฉันจะพิมพ์atโดยไม่มีข้อโต้แย้งใด ๆ เพิ่มเติม - ฉันยังคงได้รับข้อผิดพลาดเดียวกันนี้ ข้อมูลเพิ่มเติมที่นี่
Arcticooling

1
at 1mไม่ได้ทำงานให้กับ POSIX atเข้ากันได้ คำสั่งจะต้องเป็นat now + 1 minute
PiedPiper

@PiedPiper ถูกต้อง 1m เข้ากันไม่ได้กับ POSIX เวอร์ชันที่เข้ากันได้atฉันแค่สมมติว่าคุณมีเวอร์ชันที่ยอมรับไวยากรณ์นั้น
Austin Hemmelgarn

@PiedPiper, at 1mไม่ POSIX แต่ POSIX การใช้งานที่รองรับการatมีอิสระในการตีความตามที่เขาต้องการก็ไม่ได้ทำให้atการดำเนินการน้อยลงไปตามตีความมันเป็นความหมายเดียวกับnow + 1 minuteกว่ากลับข้อผิดพลาดหรือความหมายหนึ่งเดือนที่ผ่านมา IOW มันเป็นat 1mรหัสที่ไม่ใช่ POSIX
Stéphane Chazelas

2

รวมคำตอบ @ Peter Cordes และคำตอบนี้ /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687

รหัสด้านล่างมาจากคำตอบหลังโดยมีการเปลี่ยนแปลงเล็กน้อยของฉัน คอมไพล์ไฟล์ a.out (ไม่ว่าคุณจะชื่ออะไร)

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        char *tty = "/dev/tty";
        if (argc > 1)
                tty = argv[1];
        int fd = open(tty, O_WRONLY);

        int i;
        char buf[] = "\n";
        for (i = 0; i < sizeof buf - 1; i++)
                if (ioctl(fd, TIOCSTI, &buf[i]))
                        perror("ioctl");
        close(fd);
        return 0;
}

จากนั้นเรียกใช้คำสั่งด้านล่าง (ฉันใส่ a.out ใน dir / tmp / คุณอาจต้องเปลี่ยนเป็นของคุณ)

(sleep 2 && echo -e '\nhi' && /tmp/a.out &)

หรือ

(sleep 2 && echo -e '\nhi' && /tmp/a.out /proc/$$/fd/0 &)

BTW ใช้kill -WINCH $$งานได้ดีสำหรับฉันด้วย Bash 4.3 บน Gentoo


1

เมื่อสร้างคำตอบข้างต้นแล้วคุณสามารถให้คำสั่งฝังตัวส่งตรงไปยังเทอร์มินัลของคุณเช่น:

echo "echo hi >$(tty); kill -WINCH $$" | at now + 1 minute

ttyคำสั่งส่งคืนเส้นทางอุปกรณ์ของเครื่องปัจจุบันของคุณล้อมรอบใน$(...)มีทุบตีรันมันในย่อยเปลือกและคำสั่งฆ่าจะส่งสัญญาณไปยังกระบวนการปัจจุบันที่จะมีการทุบตีอีกครั้งพิมพ์ $ PS1 ของพรอมต์

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