วิธีการทำให้ Bash ทำงานหลังจากสั่งงาน


29

ฉันต้องการเรียกใช้บางสิ่งเช่นนี้:

bash -c "some_program with its arguments"

แต่เพื่อให้มีการทุบตีแบบโต้ตอบให้ทำงานหลังจากsome_programสิ้นสุด

ฉันแน่ใจว่า-cไม่ใช่วิธีที่ดีเหมือนman bashseys:

เชลล์เชิงโต้ตอบเริ่มต้นหนึ่งโดยไม่มีอาร์กิวเมนต์ที่ไม่ใช่ตัวเลือกและไม่มีตัวเลือก -c

ดังนั้นจะทำอย่างไร

เป้าหมายหลักอธิบายไว้ที่นี่

บันทึก

  • ฉันจำเป็นต้องยกเลิกsome_programเป็นครั้งคราว
  • ฉันไม่ต้องการให้มันเป็นพื้นหลัง
  • ฉันอยากอยู่ต่อbashไปเพื่อทำอย่างอื่น
  • ฉันต้องการที่จะสามารถเรียกใช้โปรแกรมอีกครั้ง

1
หากเป้าหมายนั้นซับซ้อนคุณควรอธิบายที่นี่ด้วย แต่นั่นเป็นเพียงคำแนะนำ
Kiwy

1
ฉันพยายามอธิบายให้สั้นที่สุดเท่าที่จะทำได้ที่นี่และแม่นยำมาก แต่ฉันไม่ได้คาดหวังว่าคนส่วนใหญ่จะไม่สนใจรายละเอียดและพยายามเสนอสิ่งอื่น ฉันจะใส่บันทึกย่อเพื่อให้ชัดเจน
pawel7318

เทอร์มินัลใดหนีสำหรับคำถามอื่น ๆ เป้าหมายที่คุณอธิบายนั้นเป็นไปได้ แต่จะต้องใช้การจัดการ i / o เชลล์ย่อยโดยเฉลี่ยของคุณจะไม่จัดการกับเทอร์มินัลที่หลบหนี i / o ผ่านไฟล์ปกติได้อย่างง่ายดาย คุณควรตรวจสอบpty.
mikeserv

เหตุผลเดียวที่ทำงานในคำตอบของฉันด้านล่างโดยวิธีการคือเพราะฉันขโมย terminal อย่างไรก็ตามในที่สุดกระบวนการผู้ปกครองมีแนวโน้มที่จะนำกลับมา - และจากนั้นคุณกำลังดูบัฟเฟอร์ 4kb
mikeserv

ทำไมคุณไม่ต้องการให้โปรแกรมอยู่เบื้องหลัง เริ่มต้นในพื้นหลังทำทุบตีบางใส่ไว้ในเบื้องหน้าด้วยfg
เบอร์นาร์ด

คำตอบ:


8
( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
  exec  3>&- <&4
  SCRIPT
)

สิ่งนี้ทำได้ดีกว่าจากสคริปต์ด้วยexec $0.หรือหากหนึ่งในตัวอธิบายไฟล์เหล่านั้นนำไปยังอุปกรณ์ปลายทางที่ไม่ได้ใช้งานในปัจจุบันจะช่วยได้ - คุณต้องจำไว้กระบวนการอื่น ๆ ก็ต้องการตรวจสอบเทอร์มินัลเช่นกัน

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

. ./script

เชลล์.dotและbash's sourceไม่ใช่หนึ่งเดียว - เชลล์.dotคือ POSIX ที่ระบุว่าเป็นเชลล์ในตัวพิเศษและใกล้เคียงกับการรับประกันมากที่สุดเท่าที่คุณจะได้รับแม้ว่าจะไม่ได้รับประกันว่าจะมี ...

แม้ว่าข้างต้นควรทำตามที่คุณคาดหวังกับปัญหาเล็กน้อย ตัวอย่างเช่นคุณสามารถ:

 ( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
    $(cat /path/to/script)
    exec  3>&- <&4
    SCRIPT
 )

เชลล์จะรันสคริปต์ของคุณและนำคุณกลับไปที่พรอมต์แบบโต้ตอบตราบใดที่คุณหลีกเลี่ยงexitการเชลล์จากสคริปต์ของคุณนั่นคือหรือทำให้พื้นหลังกระบวนการของคุณ - ซึ่งจะเชื่อมโยง i / o ของคุณกับ/dev/null.

การสาธิต:

% printf 'echo "%s"\n' "These lines will print out as echo" \
    "statements run from my interactive shell." \
    "This will occur before I'm given the prompt." >|/tmp/script
% ( exec sh -i 3<<SCRIPT 4<&0 <&3
    echo "do this thing"
    echo "do that thing"
    $(cat /tmp/script)
    exec  3>&- <&4
SCRIPT
)
sh-4.3$ echo "do this thing"
    do this thing
sh-4.3$ echo "do that thing"
    do that thing
sh-4.3$ echo "These lines will print out as echo"
    These lines will print out as echo
sh-4.3$ echo "statements run from my interactive shell."
    statements run from my interactive shell.
sh-4.3$ echo "This will occur before I'm given the prompt."
    This will occur before I'm given the prompt.
sh-4.3$ exec  3>&- <&4
sh-4.3$

หลาย JOBS

ฉันคิดว่าคุณควรทำความคุ้นเคยกับตัวเลือกการจัดการงานในตัวของเชลล์ @Kiwy และ @jillagre ต่างก็ได้สัมผัสกับสิ่งนี้ในคำตอบของพวกเขาแล้ว แต่มันอาจรับประกันรายละเอียดเพิ่มเติม และผมได้กล่าวแล้วหนึ่ง POSIX ระบุเปลือกพิเศษในตัว แต่set, jobs, fg,และbgมีอีกไม่กี่และเป็นคำตอบอื่นแสดงให้เห็นtrapและkillมีอีกสองคนยังคง

หากคุณยังไม่ได้รับการแจ้งเตือนทันทีเกี่ยวกับสถานะของกระบวนการที่ทำงานเบื้องหลังพร้อมกันนั่นเป็นเพราะตัวเลือกเชลล์ปัจจุบันของคุณถูกตั้งค่าเป็นค่าเริ่มต้นที่ระบุโดย POSIX ของ-mแต่คุณสามารถรับสิ่งเหล่านี้แบบอะซิงโครนัสset -bแทนได้

% man set
    b This option shall be supported if the implementation supports the
         User  Portability  Utilities  option. It shall cause the shell to
         notify the user asynchronously of background job completions. The
         following message is written to standard error:
             "[%d]%c %s%s\n", <job-number>, <current>, <status>, <job-name>

         where the fields shall be as follows:

         <current> The  character  '+' identifies the job that would be
                     used as a default for the fg or  bg  utilities;  this
                     job  can  also  be specified using the job_id "%+" or
                     "%%".  The character  '−'  identifies  the  job  that
                     would  become  the default if the current default job
                     were to exit; this job can also  be  specified  using
                     the  job_id  "%−".   For  other jobs, this field is a
                     <space>.  At most one job can be identified with  '+'
                     and  at  most one job can be identified with '−'.  If
                     there is any suspended  job,  then  the  current  job
                     shall  be  a suspended job. If there are at least two
                     suspended jobs, then the previous job also shall be a
   m  This option shall be supported if the implementation supports the
         User Portability Utilities option. All jobs shall be run in their
         own  process groups. Immediately before the shell issues a prompt
         after completion of the background job, a message  reporting  the
         exit  status  of  the background job shall be written to standard
         error. If a foreground job stops, the shell shall write a message
         to  standard  error to that effect, formatted as described by the
         jobs utility. In addition, if a job  changes  status  other  than
         exiting  (for  example,  if  it  stops  for input or output or is
         stopped by a SIGSTOP signal), the shell  shall  write  a  similar
         message immediately prior to writing the next prompt. This option
         is enabled by default for interactive shells.

คุณลักษณะพื้นฐานมากของระบบ Unix-based signalsเป็นวิธีการของพวกเขาในการจัดการกระบวนการ ฉันเคยอ่านบทความ enlighteningในเรื่องที่ likens กระบวนการนี้เพื่ออธิบาย Douglas Douglas Adams 'ของดาวเคราะห์NowWhat:

"ในคู่มือ Hitchhiker สู่กาแล็กซี่, ดักลาสอดัมส์กล่าวถึงดาวเคราะห์ที่น่าเบื่ออย่างยิ่งซึ่งอาศัยอยู่โดยกลุ่มมนุษย์ที่มีความกดดันและสัตว์บางชนิดที่มีฟันแหลมคมซึ่งสื่อสารกับมนุษย์ด้วยการกัดพวกมันอย่างหนักที่ต้นขา คล้ายกับ UNIX ซึ่งเคอร์เนลสื่อสารกับกระบวนการโดยส่งสัญญาณอัมพาตหรืออันตรายถึงขั้นตอนกระบวนการอาจดักจับสัญญาณบางอย่างและพยายามปรับให้เข้ากับสถานการณ์

kill signalsนี้จะหมายถึง

% kill -l 
> HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS

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

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

ทั้งนี้ขึ้นอยู่กับการกำหนดค่า terminal ของคุณ(ซึ่งคุณสามารถตรวจสอบกับstty -a) , CTRL+Zมีแนวโน้มที่จะกำหนดให้ส่งต่อSIGTSTPให้กับผู้นำกลุ่มกระบวนการเบื้องหน้าในปัจจุบันซึ่งมีแนวโน้มที่เปลือกของคุณและที่ยังควรจะกำหนดค่าเริ่มต้นให้trapสัญญาณที่และระงับคำสั่งสุดท้ายของคุณ อีกครั้งในขณะที่คำตอบของ @jillagre และ @Kiwy ปรากฏขึ้นพร้อมกันคุณจะไม่หยุดคุณจากการปรับฟังก์ชั่นนี้ให้เหมาะกับวัตถุประสงค์ของคุณตามที่คุณต้องการ

SCREEN JOBS

ดังนั้นเพื่อใช้ประโยชน์จากคุณสมบัติเหล่านี้คาดว่าคุณจะเข้าใจและปรับแต่งการจัดการตามความต้องการของคุณก่อน ตัวอย่างเช่นฉันเพิ่งพบscreenrc นี้ใน Githubที่มีscreenการเชื่อมโยงคีย์สำหรับSIGTSTP:

# hitting 'C-z C-z' will run Ctrl+Z (SIGTSTP, suspend as usual)
bind ^Z stuff ^Z

# hitting 'C-z z' will suspend the screen client
bind z suspend

ซึ่งจะทำให้เป็นเรื่องง่ายที่จะระงับกระบวนการที่เรียกใช้เป็นscreenกระบวนการลูกหรือscreenกระบวนการลูกเองตามที่คุณต้องการ

และทันทีหลังจากนั้น:

% fg  

หรือ:

% bg

จะเบื้องหน้าหรือเบื้องหลังกระบวนการตามที่คุณต้องการ jobsในตัวสามารถให้คุณรายการเหล่านี้ได้ตลอดเวลา การเพิ่ม-lตัวถูกดำเนินการจะรวมถึงรายละเอียด pid


ดูน่าสนใจมาก ฉันจะสามารถทดสอบทั้งหมดได้ในวันนี้
pawel7318

23

นี่เป็นวิธีแก้ปัญหาที่สั้นกว่าซึ่งทำสิ่งที่คุณต้องการได้สำเร็จ แต่อาจไม่สมเหตุสมผลเว้นแต่คุณจะเข้าใจปัญหาและวิธีการทุบตี:

bash -i <<< 'some_program with its arguments; exec </dev/tty'

การดำเนินการนี้จะเปิดเปลือก bash เริ่มต้นsome_programและหลังจากsome_programออกคุณจะถูกทิ้งลงใน bash shell

โดยพื้นฐานแล้วสิ่งที่เรากำลังทำอยู่คือการทุบตีสตริงของมันว่า STDIN some_program with its arguments; exec </dev/ttyสตริงนั่นคือ คำสั่งนี้จะบอกว่าทุบตีเพื่อเปิดใช้งานsome_programก่อนแล้วจึงรันexec </dev/ttyหลังจากนั้น /dev/ttyดังนั้นแทนที่จะดำเนินการต่อไปอ่านคำสั่งจากสตริงที่เราผ่านมันทุบตีจะเริ่มอ่านจาก

นี่-iเป็นเพราะเมื่อ bash เริ่มทำงานมันจะตรวจสอบว่า STDIN เป็น tty หรือไม่และเมื่อเริ่มต้นมันจะไม่ทำงาน แต่ในภายหลังจะเป็นเช่นนั้นดังนั้นเราจึงบังคับให้เข้าสู่โหมดโต้ตอบ


ทางออกอื่น

ความคิดอื่นที่ฉันคิดว่าจะเป็นแบบพกพามากก็คือการเพิ่มสิ่งต่อไปนี้ไว้ท้าย~/.bashrcไฟล์ของคุณ

if [[ -n "START_COMMAND" ]]; then
  start_command="$START_COMMAND"
  unset START_COMMAND
  eval "$start_command"
fi

จากนั้นเมื่อคุณต้องการเรียกใช้เชลล์ด้วยคำสั่งก่อนอื่นให้ทำดังนี้

START_COMMAND='some_program with its arguments' bash

คำอธิบาย:

ส่วนใหญ่ควรจะชัดเจน แต่ reson สำหรับสิ่งที่เปลี่ยนแปลงชื่อตัวแปรคือเพื่อให้เราสามารถ จำกัด วงตัวแปร เนื่องจาก$START_COMMANDเป็นตัวแปรที่เอ็กซ์พอร์ตมันจะสืบทอดโดยลูก ๆ ของเชลล์และหากเชลล์ bash อื่นเป็นหนึ่งในลูกเหล่านั้นมันจะรันคำสั่งอีกครั้ง ดังนั้นเราจึงกำหนดค่าให้กับตัวแปรที่ไม่ได้เอ็กซ์พอร์ตใหม่ ( $start_command) และลบอันเก่า


ระเบิดถ้ามีอะไรมากกว่าหนึ่ง? คำสั่งเดียว? ไม่ควรทำงานได้ คุณพูดถูกเกี่ยวกับการพกพา มี 2 ปัจจัยที่จะมีที่<<<ไม่ได้เป็น POSIX แต่คุณสามารถecho "string here" | bash -iแทน จากนั้นมีสิ่ง/dev/ttyที่เป็นลินุกซ์ แต่คุณสามารถทำซ้ำ FD ก่อนที่จะเปิดตัว bash จากนั้นเปิด STDIN อีกครั้งซึ่งดูคล้ายกับสิ่งที่คุณทำ แต่ฉันเลือกที่จะทำสิ่งต่าง ๆ ให้เรียบง่าย
Patrick

ตกลงตกลงเพียงแค่ไม่ได้ต่อสู้เพียงแค่นี้ได้ผลสำหรับฉันและทำทั้งหมดที่ฉันต้องการ ฉันไม่สนใจ POSIX และการพกพามากนักฉันต้องการมันในกล่องและที่นั่นเท่านั้น ฉันจะตรวจสอบคำตอบของ mikeserv ด้วย แต่ตอนนี้ไม่สามารถทำได้
pawel7318

1
ไม่ต่อสู้ :-) ฉันเคารพคำตอบของ mikserv เขาเข้ากันได้ฉันไปเพื่อความเรียบง่าย ทั้งสองถูกต้องสมบูรณ์ บางครั้งฉันจะเข้ากันได้ถ้ามันไม่ซับซ้อนเกินไป ในกรณีนี้ฉันไม่คิดว่ามันจะคุ้มค่า
Patrick

2
@Patrick /dev/ttyไม่ใช่สิ่งที่ Linux แต่ POSIX แน่นอน
jlliagre

2
เฮ้whaddayaknow
แพทริค

8

สิ่งนี้ควรทำเคล็ดลับ:

bash -c "some_program with its arguments;bash"

แก้ไข:

นี่คือความพยายามใหม่หลังจากติดตามการอัปเดตของคุณ:

bash -c "
trap 'select wtd in bash restart exit; do [ \$wtd = restart ] && break || \$wtd ; done' 2
while true; do
    some_program with its arguments
done
"
  • ฉันต้องยกเลิก some_program เป็นครั้งคราว

ใช้ControlCคุณจะเห็นเมนูเล็ก ๆ นี้:

1) bash
2) restart
3) exit
  • ฉันไม่ต้องการให้มันเป็นพื้นหลัง

เป็นเช่นนั้น

  • ฉันต้องการอยู่ในการทุบตีแล้วทำอย่างอื่น

เลือกตัวเลือก "ทุบตี"

  • ฉันต้องการที่จะสามารถเรียกใช้โปรแกรมอีกครั้ง

เลือกตัวเลือก "เริ่มใหม่"


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

สิ่งนี้จะเรียกใช้งานเชลล์ย่อย ฉันคิดว่าผู้ถามพยายามรักษาสภาพแวดล้อมของสคริปต์
mikeserv

ขอบคุณ jlliagre - ความพยายามดี แต่ไม่ค่อยมีประโยชน์สำหรับฉัน ฉันมักจะกด ctrl + C และฉันคาดหวังว่ามันจะทำในสิ่งที่มันควรจะทำ เมนูเพิ่มเติมเป็นเพียงมาก
pawel7318

@ pawel7318 ลักษณะการทำงานของเป็นเพียงผลข้างเคียงของการกำหนดค่าเริ่มต้นขั้วของคุณที่จะตีความว่ามันเป็นCTRL+C คุณสามารถปรับเปลี่ยนว่าคุณจะมีSIGINT stty
mikeserv

@ pawel7318 เพื่อชี้แจงSIGINTเป็นข้างต้นด้วยtrapped 2
mikeserv

3

คุณสามารถทำได้โดยส่งสคริปต์ของคุณเป็นไฟล์เริ่มต้น:

bash --init-file foo.script

หรือคุณสามารถส่งผ่านบนบรรทัดคำสั่ง:

bash --init-file <(echo "ls -al")

โปรดทราบว่า--init-fileมีไว้สำหรับการอ่านไฟล์การกำหนดค่าเริ่มต้นทั้งระบบเช่น/etc/bash.bashrcคุณอาจต้องการ ' source' เหล่านี้ในสคริปต์


0

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

bash -c "some_program with its arguments; bash"

นี่จะเป็นการเปิดใช้งาน bash แบบโต้ตอบหลังจากโปรแกรมรัน


ที่เปิดตัว subshell
mikeserv

0

คุณสามารถใส่คำสั่งเป็นแบ็คกราวน์เพื่อเปิด bash ปัจจุบันของคุณ:

some_program with its arguments &

หากต้องการกลับไปที่การทำงานคุณสามารถใช้ fgคำสั่งและ^+zวางไว้ในพื้นหลังได้อีกครั้ง


ไม่ใช่เวลานี้. คุณคิดว่าฉันต้องการเรียกใช้ bash นี้ ... จาก bash ซึ่งไม่ใช่กรณี
pawel7318

@ pawel7318 ถ้าคุณอธิบายเพิ่มเติมอีกเล็กน้อยเราสามารถให้คำตอบที่ดีกว่ากับคุณได้ไหม?
Kiwy

กรุณาตรวจสอบอีกคำถามของฉันที่นี่
pawel7318

@ pawel7318 หากคำถามของคุณเกี่ยวข้องโปรดเพิ่มลิงก์ไปยังคำถามอื่นในคำถามของคุณเอง
Kiwy

1
@ pawel7318 bashหรือไม่คุณกำลังใช้เชลล์มนุษย์ต่างดาวมากสำหรับฉันถ้ามันไม่สามารถจัดการแบ็คกราวน์ของกระบวนการ และฉันเห็นด้วยกับกีวี - ข้อมูลนั้นจะให้บริการคุณได้ดีขึ้นถ้ามันเป็นคำถามของคุณ
mikeserv

0

คุณอาจต้องการใช้หน้าจอเพื่อเรียกใช้คำสั่ง คุณสามารถแนบเซสชันอีกครั้งหลังจากที่คำสั่งเสร็จสิ้น

some_program with its arguments&อีกวิธีหนึ่งคือเพียงแค่เรียกใช้คำสั่งในพื้นหลัง สิ่งนี้จะทำให้คุณมีความสามารถในการรันคำสั่งอีกครั้งและรับสถานะของคำสั่งเมื่อเสร็จสิ้น


ฉันกำลังเรียกใช้จากนั้นscreenแต่การวางไว้ที่พื้นหลังนั้นไม่มีประโยชน์สำหรับฉัน - บางครั้งฉันจำเป็นต้องยกเลิกโปรแกรมทำอย่างอื่นแล้วเรียกใช้อีกครั้ง และเป้าหมายหลักคือการทำสิ่งนี้ให้รวดเร็ว
pawel7318

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