Làm thế nào để giữ Bash chạy sau khi thực hiện lệnh?


29

Tôi muốn chạy một cái gì đó như thế này:

bash -c "some_program with its arguments"

nhưng để có một bash tương tác tiếp tục chạy sau khi some_programkết thúc.

Tôi chắc chắn -ckhông phải là một cách tốt như man bashseys:

Một shell tương tác được bắt đầu mà không có đối số không phải tùy chọn và không có tùy chọn -c

Vậy làm thế nào để làm điều này?

Mục tiêu chính được mô tả ở đây

CHÚ THÍCH

  • Tôi cần phải chấm dứt some_programtheo thời gian
  • Tôi không muốn đặt nó vào nền
  • Tôi muốn ở lại bashsau đó để làm một cái gì đó khác
  • Tôi muốn có thể chạy lại chương trình

1
Nếu mục tiêu là phức tạp, bạn cũng nên giải thích nó ở đây. nhưng đó chỉ là một lời khuyên
Kiwy

1
Tôi đã cố gắng mô tả càng ngắn càng tốt ở đây và rất chính xác. Nhưng tôi không hy vọng rằng hầu hết mọi người sẽ không tập trung vào các chi tiết và sẽ cố gắng đề xuất một cái gì đó khác. Tôi sẽ đặt một số ghi chú để làm cho nó rõ ràng.
pawel7318

Các thiết bị đầu cuối thoát cho câu hỏi khác là gì? Mục tiêu bạn mô tả là có thể thực hiện được, nhưng nó sẽ yêu cầu xử lý i / o. Subshell trung bình của bạn sẽ không dễ dàng xử lý i / o thiết bị đầu cuối thoát thông qua một tập tin thông thường. Bạn nên xem xétpty.
mikeerv

Nhân tiện, lý do duy nhất nó hoạt động trong câu trả lời của tôi dưới đây là vì tôi ăn cắp thiết bị đầu cuối. Cuối cùng, mặc dù, quá trình cha có thể lấy lại - và sau đó bạn đang xem bộ đệm 4kb.
mikeerv

Tại sao bạn không muốn đặt một chương trình trong nền? Bắt đầu nó ở chế độ nền, thực hiện một số bash, đặt nó lên nền trước vớifg
Bernhard

Câu trả lời:


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

Điều này được thực hiện tốt hơn từ một tập lệnh mặc dù với exec $0.Hoặc nếu một trong những mô tả tệp đó hướng đến một thiết bị đầu cuối hiện không được sử dụng thì nó sẽ giúp - bạn cũng phải nhớ, các quy trình khác cũng muốn kiểm tra thiết bị đầu cuối đó.

Và nhân tiện, nếu mục tiêu của bạn là, như tôi nghĩ là, để bảo vệ môi trường của tập lệnh sau khi thực hiện nó, có lẽ bạn sẽ được phục vụ tốt hơn rất nhiều với:

. ./script

Vỏ .dotbash's sourcekhông phải là một và giống nhau - vỏ .dotlà POSIX được chỉ định là vỏ được chế tạo đặc biệt và do đó gần như được bảo đảm như bạn có thể nhận được, mặc dù điều này không có nghĩa là đảm bảo nó sẽ ở đó ...

Mặc dù những điều trên nên làm như bạn mong đợi với ít vấn đề. Chẳng hạn, bạn có thể:

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

Trình bao sẽ chạy tập lệnh của bạn và đưa bạn trở lại dấu nhắc tương tác - miễn là bạn tránh exitlấy trình bao từ tập lệnh của mình, nghĩa là hoặc làm nền cho quy trình của bạn - điều đó sẽ liên kết i / o của bạn với/dev/null.

BẢN GIỚI THIỆU:

% 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$

NHIỀU JOBS

Theo ý kiến ​​của tôi, bạn nên làm quen một chút với các tùy chọn quản lý tác vụ tích hợp của shell. @Kiwy và @jillagre đều đã chạm vào điều này trong câu trả lời của họ, nhưng nó có thể đảm bảo thêm chi tiết. Và tôi đã đề cập đến một trình bao đặc biệt được tích hợp theo POSIX, nhưng set, jobs, fg,bgmột vài câu nữa, và, như một câu trả lời khác chứng minh trapkillvẫn còn hai câu nữa.

Nếu bạn chưa nhận được thông báo tức thì về trạng thái của các quá trình chạy nền đồng thời, thì đó là vì các tùy chọn vỏ hiện tại của bạn được đặt thành mặc định do POSIX chỉ định -m, nhưng set -bthay vào đó bạn có thể nhận được các đồng bộ này một cách không đồng 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.

Một tính năng rất cơ bản của các hệ thống dựa trên Unix là phương pháp xử lý của chúng signals. Có lần tôi đã đọc một bài viết khai sáng về chủ đề tương tự quá trình này với mô tả về hành tinh của Douglas Adams Bây giờ:

"Trong Hướng dẫn về thiên hà của Hitchhiker, Douglas Adams đã đề cập đến một hành tinh cực kỳ buồn tẻ, nơi sinh sống của một nhóm người bị trầm cảm và một giống động vật nào đó có hàm răng sắc nhọn giao tiếp với con người bằng cách cắn chúng rất mạnh vào đùi. tương tự như UNIX, trong đó hạt nhân giao tiếp với các quá trình bằng cách gửi tín hiệu tê liệt hoặc gây chết người cho chúng. Các quy trình có thể chặn một số tín hiệu và cố gắng thích ứng với tình huống, nhưng hầu hết chúng đều không. "

Đây là đề cập đến 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

Ít nhất là đối với tôi, trích dẫn ở trên đã trả lời rất nhiều câu hỏi. Chẳng hạn, tôi luôn coi nó rất lạ và hoàn toàn không trực quan rằng nếu tôi muốn theo dõi một ddquá trình tôi phải thực hiện kill. Sau khi đọc nó có ý nghĩa.

Tôi muốn nói rằng hầu hết trong số họ không cố gắng thích nghi vì lý do chính đáng - điều đó có thể gây phiền toái lớn hơn nhiều so với việc có một loạt các quy trình spam thiết bị đầu cuối của bạn với bất kỳ thông tin nào mà nhà phát triển của họ nghĩ là quan trọng đối với bạn .

Tùy thuộc vào cấu hình thiết bị đầu cuối của bạn (mà bạn có thể kiểm tra stty -a) , CTRL+Zcó khả năng được đặt để chuyển tiếp SIGTSTPđến người lãnh đạo nhóm quy trình tiền cảnh hiện tại, có khả năng là vỏ của bạn và cũng nên được cấu hình theo mặc định cho traptín hiệu đó và tạm dừng lệnh cuối cùng của bạn. Một lần nữa, như các câu trả lời của @jillagre và @Kiwy cùng hiển thị, không có gì ngăn cản bạn điều chỉnh chức năng này theo mục đích của bạn như bạn muốn.

SCREEN JOBS

Vì vậy, để tận dụng các tính năng này, trước tiên bạn cần hiểu chúng và tùy chỉnh xử lý chúng theo nhu cầu của riêng bạn. Ví dụ: tôi vừa tìm thấy screenrc này trên Github bao gồm screencác ràng buộc khóa cho 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

Điều đó sẽ làm cho nó trở thành một vấn đề đơn giản để đình chỉ một quá trình đang chạy như một screenquá trình con hoặc screenchính quá trình con đó như bạn muốn.

Và ngay lập tức sau đó:

% fg  

HOẶC LÀ:

% bg

Sẽ tiền cảnh hoặc nền quá trình như bạn muốn. Các jobsbuilt-in có thể cung cấp cho bạn một danh sách các bất cứ lúc nào. Thêm -ltoán hạng sẽ bao gồm chi tiết pid.


Trông rất thú vị. Tôi sẽ có thể kiểm tra tất cả sau này ngày hôm nay.
pawel7318

23

Đây là một giải pháp ngắn hơn để thực hiện những gì bạn muốn, nhưng có thể không có ý nghĩa trừ khi bạn hiểu vấn đề và cách thức hoạt động của bash:

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

Điều này sẽ khởi chạy một bash shell, bắt đầu some_programvà sau khi some_programthoát, bạn sẽ được thả vào một bash shell.

Về cơ bản những gì chúng tôi đang làm là cho ăn một chuỗi trên STDIN. Chuỗi đó là some_program with its arguments; exec </dev/tty. Điều này nói với bash để khởi chạy some_programtrước, và sau đó chạy exec </dev/ttysau. Vì vậy, thay vì tiếp tục đọc các lệnh từ chuỗi chúng ta chuyển qua, bash sẽ bắt đầu đọc từ đó /dev/tty.

Các -ilà bởi vì khi bash khởi động, nó sẽ kiểm tra xem nếu STDIN là một tty, và khi nó bắt đầu nó không phải. Nhưng sau này nó sẽ như vậy, vì vậy chúng tôi buộc nó vào chế độ tương tác.


Giải pháp khác

Một ý tưởng khác mà tôi nghĩ rằng nó sẽ rất dễ mang theo là thêm phần sau vào cuối ~/.bashrctập tin của bạn .

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

Sau đó, khi bạn muốn khởi chạy shell bằng lệnh trước, chỉ cần thực hiện:

START_COMMAND='some_program with its arguments' bash

Giải trình:

Hầu hết điều này là rõ ràng, nhưng cộng hưởng cho các công cụ thay đổi tên biến là để chúng ta có thể bản địa hóa biến. Vì $START_COMMANDlà một biến được xuất, nó sẽ được thừa kế bởi bất kỳ con nào của shell và nếu một bash shell khác là một trong những con đó, nó sẽ chạy lại lệnh. Vì vậy, chúng tôi gán giá trị cho một biến không được báo cáo mới ( $start_command) và xóa biến cũ.


Thổi lên nếu có nhiều hơn một cái gì? Một lệnh? không, vẫn nên làm việc Bạn nói đúng về tính di động mặc dù. Có 2 yếu tố ở đó, <<<không phải là POSIX, nhưng echo "string here" | bash -ithay vào đó bạn có thể . Sau đó, /dev/ttyđó là một điều linux. Nhưng bạn có thể nhân đôi FD trước khi khởi chạy bash, sau đó mở lại STDIN, trông giống như những gì bạn đang làm, nhưng tôi đã chọn giữ mọi thứ đơn giản.
Patrick

ok ok chỉ không chiến đấu. Điều này chỉ làm việc cho tôi và làm tất cả những gì tôi cần. Tôi không quan tâm nhiều đến POSIX và tính di động, tôi cần nó trên hộp của mình và chỉ ở đó. Tôi cũng sẽ kiểm tra câu trả lời của mikeerv nhưng không thể làm điều này ngay bây giờ.
pawel7318

1
Không đánh nhau :-). Tôi tôn trọng câu trả lời của mikserv. Anh ấy đi cho tương thích, tôi đi cho đơn giản. Cả hai đều hoàn toàn hợp lệ. Đôi khi tôi sẽ đi để tương thích nếu nó không quá phức tạp. Trong trường hợp này tôi đã không nghĩ rằng nó là giá trị nó.
Patrick

2
@Patrick, /dev/ttykhông phải là một thứ Linux nhưng chắc chắn là POSIX.
jlliagre

2
Này, whaddayaknow .
Patrick

8

Cái này cần phải dùng mẹo:

bash -c "some_program with its arguments;bash"

Chỉnh sửa:

Đây là một nỗ lực mới sau bản cập nhật của bạn:

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
"
  • Thỉnh thoảng tôi cần chấm dứt some_program

Sử dụng ControlC, bạn sẽ được trình bày menu nhỏ này:

1) bash
2) restart
3) exit
  • Tôi không muốn đặt nó vào nền

Đó là trường hợp.

  • Tôi muốn ở lại bash sau đó để làm một cái gì đó khác

Chọn lựa chọn "bash"

  • Tôi muốn có thể chạy lại chương trình

Chọn lựa chọn "khởi động lại"


không tệ không xấu .. nhưng cũng sẽ tốt để có thể quay trở lại mệnh lệnh cuối cùng đó. Bất cứ ý tưởng cho điều đó? Vui lòng kiểm tra một câu hỏi khác của tôi ở đây để xem những gì tôi cần nó. Vì vậy, có thể bạn sẽ đề xuất một cách tiếp cận khác
pawel7318

Điều này thực hiện một subshell. Tôi nghĩ rằng người hỏi đang cố gắng duy trì môi trường kịch bản.
mikeerv

cảm ơn bạn jlliagre - nỗ lực tốt đẹp nhưng không hữu ích cho tôi nhiều. Tôi thường nhấn ctrl + C và tôi hy vọng nó sẽ làm những gì nó cho là sẽ làm. Thực đơn bổ sung chỉ là nhiều.
pawel7318

@ pawel7318 hành vi của CTRL+Cchỉ là một tác dụng phụ của cấu hình mặc định của thiết bị đầu cuối của bạn để hiểu nó là một SIGINT. Bạn có thể sửa đổi điều đó như bạn muốn stty.
mikeerv

@ pawel7318 để làm rõ hơn, SIGINTtrappedở trên với 2.
mikeerv

3

Bạn có thể làm điều đó bằng cách chuyển tập lệnh của bạn dưới dạng tệp khởi tạo:

bash --init-file foo.script

Hoặc bạn có thể chuyển nó trên dòng lệnh:

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

Lưu ý rằng điều đó --init-filecó nghĩa là để đọc các tệp khởi tạo trên toàn hệ thống như /etc/bash.bashrcvậy, vì vậy bạn có thể muốn ' source' chúng trong tập lệnh của mình.


0

Tôi thực sự không thấy quan điểm của việc này, vì bạn đã quay trở lại vỏ sau khi chạy chương trình này. Tuy nhiên, bạn có thể làm điều này:

bash -c "some_program with its arguments; bash"

Điều này sẽ khởi chạy một bash tương tác sau khi chương trình chạy.


Điều đó khởi động một subshell.
mikeerv

0

Bạn có thể đặt lệnh ở chế độ nền để giữ cho bash hiện tại của bạn mở:

some_program with its arguments &

Để trở lại hoạt động, sau đó bạn có thể sử dụng fglệnh và ^+zđặt lại vào nền


không phải lúc này. Bạn cho rằng tôi muốn chạy bash này ... từ bash không phải là trường hợp.
pawel7318

@ pawel7318 nếu bạn giải thích thêm một chút, chúng tôi có thể cung cấp cho bạn câu trả lời tốt hơn không?
Kiwy

Vui lòng kiểm tra một câu hỏi khác của tôi ở đây .
pawel7318

@ pawel7318 nếu câu hỏi của bạn có liên quan, vui lòng thêm liên kết đến câu hỏi khác trong câu hỏi của riêng bạn.
Kiwy

1
@ pawel7318 bashhay không, bạn đang sử dụng trình bao rất xa lạ với tôi nếu nó không thể xử lý quá trình nền. Và tôi đồng ý với Kiwi - thông tin đó sẽ phục vụ bạn tốt hơn nếu có trong câu hỏi của bạn.
mikeerv

0

Bạn có thể muốn sử dụng màn hình để chạy lệnh. Sau đó, bạn có thể gắn lại vào phiên sau khi lệnh hoàn thành.

Ngoài ra, chỉ cần chạy lệnh trong nền some_program with its arguments&. Điều này sẽ cho bạn khả năng chạy lại lệnh và nhận trạng thái của lệnh sau khi hoàn thành.


Tôi đang chạy nó chính xác từ screennhưng đặt nó vào nền không hữu ích cho tôi - đôi khi tôi cần chấm dứt chương trình, làm một cái gì đó khác và chạy lại nó. Và mục tiêu chính là làm điều này nhanh chóng.
pawel7318

@ pawel7318 Bạn có thể tắt chương trình nền bằng lệnh kill %hoặc kill %1.
BillThor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.