Bagaimana agar Bash tetap berjalan setelah eksekusi perintah?


29

Saya ingin menjalankan sesuatu seperti ini:

bash -c "some_program with its arguments"

tetapi untuk memiliki bash interaktif tetap berjalan setelah selesai some_program.

Saya yakin -cini bukan cara yang baik seperti man bashseys:

Shell interaktif adalah yang dimulai tanpa argumen non-opsi dan tanpa opsi -c

Jadi bagaimana melakukan ini?

Tujuan utamanya dijelaskan di sini

CATATAN

  • Saya perlu mengakhiri some_programdari waktu ke waktu
  • Saya tidak ingin meletakkannya di latar belakang
  • Saya ingin tetap di sana bashuntuk melakukan sesuatu yang lain
  • Saya ingin dapat menjalankan program lagi

1
jika tujuannya kompleks, Anda juga harus menjelaskannya di sini. tapi itu hanya saran
Kiwy

1
Saya mencoba menggambarkan sesingkat mungkin di sini dan sangat tepat. Tetapi saya tidak berharap bahwa kebanyakan orang tidak akan fokus pada detail dan akan mencoba mengusulkan sesuatu yang lain. Saya akan menaruh beberapa catatan untuk membuatnya jelas.
pawel7318

Untuk apa terminal lolos dalam pertanyaan lain itu? Tujuan yang Anda uraikan dapat dilakukan, tetapi akan membutuhkan penanganan i / o. Subshell rata-rata Anda tidak akan dengan mudah menangani terminal lolos i / o melalui file biasa. Anda harus melihat kepty.
mikeserv

Satu-satunya alasan itu bekerja dalam jawaban saya di bawah, omong-omong, adalah karena saya mencuri terminal. Namun, akhirnya, proses induk kemungkinan akan mengambilnya kembali - dan kemudian Anda melihat buffer 4kb.
mikeserv

Mengapa Anda tidak ingin meletakkan program di latar belakang? Mulailah dari latar belakang, lakukan beberapa bash, letakkan di latar depan denganfg
Bernhard

Jawaban:


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

Ini lebih baik dilakukan dari skrip dengan exec $0.Atau jika salah satu deskriptor file mengarahkan ke perangkat terminal yang saat ini sedang digunakan itu akan membantu - Anda harus ingat, proses lain ingin memeriksa terminal itu juga.

Dan omong-omong, jika tujuan Anda adalah, seperti yang saya asumsikan, untuk melestarikan lingkungan skrip setelah menjalankannya, Anda mungkin akan jauh lebih baik dilayani dengan:

. ./script

Shell .dotdan bash's sourcebukan satu dan sama - shell .dotPOSIX ditentukan sebagai shell khusus builtin dan karena itu hampir dijamin seperti yang Anda dapatkan, meskipun ini tidak berarti jaminan itu akan ada di sana ...

Meskipun hal di atas harus dilakukan seperti yang Anda harapkan dengan sedikit masalah. Misalnya, Anda dapat:

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

Shell akan menjalankan skrip Anda dan mengembalikan Anda ke prompt interaktif - selama Anda menghindari exitshell dari skrip Anda, yaitu, atau melatar belakangi proses Anda - yang akan menautkan i / o Anda ke/dev/null.

DEMO:

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

BANYAK JOBS

Menurut pendapat saya, Anda harus lebih terbiasa dengan opsi manajemen tugas bawaan shell. @Kiwy dan @jillagre telah menyinggung ini dalam jawaban mereka, tetapi mungkin diperlukan detail lebih lanjut. Dan saya sudah menyebutkan satu shell khusus POSIX yang ditentukan built-in, tetapi set, jobs, fg,dan bgbeberapa lagi, dan, sebagai jawaban lain menunjukkan trapdan killmasih dua lagi.

Jika Anda belum menerima pemberitahuan instan tentang status menjalankan proses berlatar belakang secara bersamaan, itu karena opsi shell Anda saat ini disetel ke default POSIX yang ditentukan -m, tetapi Anda dapat memperolehnya secara asinkron dengan 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.

Fitur yang sangat mendasar dari sistem berbasis Unix adalah metode proses penanganannya signals. Saya pernah membaca sebuah artikel yang mencerahkan tentang subjek yang menyamakan proses ini dengan deskripsi planet Douglas Now tentang What NowHhat:

"Dalam The Hitchhiker's Guide to the Galaxy, Douglas Adams menyebutkan sebuah planet yang sangat membosankan, dihuni oleh sekelompok manusia yang tertekan dan beberapa jenis hewan dengan gigi tajam yang berkomunikasi dengan manusia dengan menggigit mereka sangat keras di paha. Ini sangat mengejutkan mirip dengan UNIX, di mana kernel berkomunikasi dengan proses dengan mengirimkan sinyal melumpuhkan atau mematikan kepada mereka. Proses dapat mencegat beberapa sinyal, dan mencoba untuk beradaptasi dengan situasi, tetapi kebanyakan dari mereka tidak. "

Ini mengacu pada 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

Setidaknya bagi saya, kutipan di atas menjawab banyak pertanyaan. Sebagai contoh, saya selalu menganggapnya sangat aneh dan sama sekali tidak intuitif bahwa jika saya ingin memonitor suatu ddproses saya harus melakukannya kill. Setelah membaca itu masuk akal.

Saya akan mengatakan sebagian besar dari mereka tidak mencoba untuk beradaptasi karena alasan yang baik - itu bisa menjadi gangguan yang jauh lebih besar daripada itu akan menjadi anugerah untuk memiliki banyak proses spam terminal Anda dengan informasi apa pun yang menurut pengembang mereka mungkin penting bagi Anda .

Tergantung pada konfigurasi terminal Anda (yang dapat Anda periksa dengan stty -a) , CTRL+Zkemungkinan diatur untuk meneruskan a SIGTSTPke pemimpin grup proses latar depan saat ini, yang kemungkinan shell Anda, dan yang juga harus dikonfigurasi secara default untuk trapsinyal itu dan menangguhkan perintah terakhir Anda. Sekali lagi, seperti yang ditunjukkan oleh jawaban @jillagre dan @Kiwy bersama, tidak ada yang dapat menghentikan Anda dari menyesuaikan fungsi ini dengan tujuan yang Anda inginkan.

SCREEN JOBS

Jadi untuk memanfaatkan fitur-fitur ini, diharapkan Anda terlebih dahulu memahaminya dan menyesuaikan penanganannya dengan kebutuhan Anda sendiri. Sebagai contoh, saya baru saja menemukan screenrc ini di Github yang menyertakan screenbinding kunci untuk 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

Itu akan membuatnya menjadi masalah sederhana untuk menunda proses yang berjalan sebagai screenproses anak atau screenproses anak itu sendiri seperti yang Anda inginkan.

Dan segera sesudahnya:

% fg  

ATAU:

% bg

Apakah latar depan atau latar belakang proses yang Anda inginkan. The jobsbuilt-in dapat memberikan Anda daftar ini setiap saat. Menambahkan -loperan akan mencakup detail pid.


Terlihat sangat menarik. Saya akan bisa menguji semuanya nanti hari ini.
pawel7318

23

Inilah solusi yang lebih singkat yang memenuhi apa yang Anda inginkan, tetapi mungkin tidak masuk akal kecuali Anda memahami masalahnya dan bagaimana bash bekerja:

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

Ini akan meluncurkan bash shell, mulai some_program, dan setelah some_programkeluar, Anda akan jatuh ke bash shell.

Pada dasarnya yang kami lakukan adalah memberi makan bash string pada STDIN-nya. String itu some_program with its arguments; exec </dev/tty. Ini memberitahu bash untuk meluncurkan some_programterlebih dahulu, dan kemudian menjalankannya exec </dev/tty. Jadi, alih-alih terus membaca perintah dari string yang kami lewati, bash akan mulai membaca dari /dev/tty.

Itu -ikarena ketika bash mulai, itu memeriksa untuk melihat apakah STDIN adalah tty, dan ketika mulai itu bukan. Tapi nanti akan jadi, jadi kami memaksanya ke mode interaktif.


Solusi lain

Gagasan lain yang saya pikir akan sangat portabel adalah menambahkan berikut ini di akhir ~/.bashrcfile Anda .

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

Lalu ketika Anda ingin meluncurkan shell dengan perintah pertama, lakukan saja:

START_COMMAND='some_program with its arguments' bash

Penjelasan:

Sebagian besar harus jelas, tetapi resonansi untuk hal-hal yang mengubah nama variabel adalah agar kita dapat melokalkan variabel. Karena $START_COMMANDmerupakan variabel yang diekspor, itu akan diwarisi oleh anak-anak dari shell, dan jika bash shell lain adalah salah satu dari anak-anak itu, itu akan menjalankan perintah lagi. Jadi kami menetapkan nilai ke variabel baru yang tidak diekspor ( $start_command) dan menghapus yang lama.


Meledakkan jika ada lebih dari satu apa? Satu perintah? tidak, masih harus bekerja. Anda benar tentang portabilitas. Ada 2 faktor di sana, <<<bukan POSIX, tetapi Anda bisa melakukannya echo "string here" | bash -i. Lalu ada /dev/ttyyang merupakan hal linux. Tapi Anda bisa menipu FD sebelum meluncurkan bash, dan kemudian buka kembali STDIN, yang terlihat mirip dengan apa yang Anda lakukan, tapi saya memilih untuk menjaga hal-hal sederhana.
Patrick

ok ok jangan berkelahi. Ini hanya bekerja untuk saya dan melakukan semua yang saya butuhkan. Saya tidak terlalu peduli tentang POSIX dan portabilitas, saya membutuhkannya di komputer saya dan hanya ada di sana. Saya akan memeriksa jawaban mikeserv juga tetapi tidak bisa melakukan ini sekarang.
pawel7318

1
Tidak berkelahi :-). Saya menghargai jawaban mikserv. Dia pergi untuk kompatibilitas, saya pergi untuk kesederhanaan. Keduanya sepenuhnya valid. Terkadang saya akan mencari kompatibilitas jika tidak terlalu rumit. Dalam hal ini saya tidak berpikir itu sepadan.
Patrick

2
@ Patrick, /dev/ttybukan hal yang Linux tapi pasti POSIX.
jlliagre

2

8

Ini harus melakukan trik:

bash -c "some_program with its arguments;bash"

Edit:

Ini adalah upaya baru mengikuti pembaruan Anda:

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
"
  • Saya perlu menghentikan some_program dari waktu ke waktu

Gunakan ControlC, Anda akan disajikan menu kecil ini:

1) bash
2) restart
3) exit
  • Saya tidak ingin meletakkannya di latar belakang

Itu masalahnya.

  • Saya ingin tetap di bash kemudian melakukan sesuatu yang lain

Pilih pilihan "bash"

  • Saya ingin dapat menjalankan program lagi

Pilih pilihan "mulai ulang"


tidak buruk tidak buruk .. tetapi akan lebih baik juga untuk dapat kembali ke perintah terakhir itu. Ada ide untuk itu? Silakan periksa pertanyaan saya yang lain di sini untuk melihat untuk apa saya membutuhkannya. Jadi mungkin Anda akan menyarankan pendekatan lain
pawel7318

Ini mengeksekusi subkulit. Saya pikir si penanya mencoba melestarikan lingkungan skrip.
mikeserv

Terima kasih jlliagre - usaha yang bagus tetapi tidak banyak berguna bagi saya. Saya biasanya menekan ctrl + C dan saya berharap untuk hanya melakukan apa yang seharusnya dilakukan. Menu tambahan hanya untuk banyak.
pawel7318

@ pawel7318 perilaku CTRL+Chanyalah efek samping dari konfigurasi default terminal Anda untuk menafsirkannya sebagai a SIGINT. Anda dapat mengubahnya sesuai keinginan stty.
mikeserv

@ pawel7318 untuk lebih memperjelas, SIGINTada di trappedatas dengan 2.
mikeserv

3

Anda dapat melakukannya dengan mengirimkan skrip Anda sebagai file inisialisasi:

bash --init-file foo.script

Atau Anda bisa meneruskannya di baris perintah:

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

Catatan yang --init-filedimaksudkan untuk membaca file inisialisasi lebar sistem seperti /etc/bash.bashrcjadi Anda mungkin ingin ' source' ini dalam skrip Anda.


0

Saya benar-benar tidak mengerti gunanya melakukan ini, karena Anda sudah kembali ke shell setelah menjalankan program ini. Namun, Anda bisa melakukannya:

bash -c "some_program with its arguments; bash"

Ini akan meluncurkan bash interaktif setelah program berjalan.


Itu meluncurkan subkulit.
mikeserv

0

Anda bisa meletakkan perintah di latar belakang untuk menjaga bash Anda saat ini terbuka:

some_program with its arguments &

Untuk kembali ke menjalankan Anda kemudian dapat menggunakan fgperintah dan ^+zmeletakkannya di latar belakang lagi


tidak kali ini. Anda menganggap saya ingin menjalankan bash ini ... dari bash yang tidak demikian.
pawel7318

@ pawel7318 jika Anda menjelaskan lebih banyak, kami dapat memberikan Anda jawaban yang lebih baik mungkin juga?
Kiwy

Silakan periksa pertanyaan saya yang lain di sini .
pawel7318

@ pawel7318 jika pertanyaan Anda terkait, tambahkan tautan ke pertanyaan lain dalam pertanyaan Anda sendiri.
Kiwy

1
@ pawel7318 bashatau tidak, Anda menggunakan shell yang sangat asing bagi saya jika tidak bisa menangani proses backgrounding. Dan saya setuju dengan Kiwi - informasi itu akan membantu Anda lebih baik jika itu ada dalam pertanyaan Anda.
mikeserv

0

Anda mungkin ingin menggunakan layar untuk menjalankan perintah. Anda kemudian dapat menyambung kembali ke sesi setelah perintah selesai.

Atau, jalankan perintah di latar belakang some_program with its arguments&. Ini akan memberi Anda kemampuan untuk menjalankan kembali perintah dan mendapatkan status perintah setelah selesai.


Saya menjalankannya tepat dari screentetapi meletakkannya di latar belakang tidak berguna bagi saya - saya kadang-kadang perlu menghentikan program, melakukan sesuatu yang lain dan menjalankannya lagi. Dan tujuan utamanya adalah melakukan ini dengan cepat.
pawel7318

@ pawel7318 Anda dapat mematikan program latar belakang dengan perintah kill %atau kill %1.
BillThor
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.