Jalankan banyak perintah dan bunuh sebagai satu di bash

52

Saya ingin menjalankan beberapa perintah (proses) pada satu shell. Semuanya memiliki output berkelanjutan sendiri dan tidak berhenti. Menjalankan mereka di istirahat latar belakang Ctrl- C. Saya ingin menjalankannya sebagai satu proses (subkulit, mungkin?) Untuk dapat menghentikan semuanya Ctrl- C.

Untuk lebih spesifik, saya ingin menjalankan tes unit dengan mocha(mode menonton), menjalankan server dan menjalankan beberapa preprocessing file (mode menonton) dan melihat output masing-masing dalam satu jendela terminal. Pada dasarnya saya ingin menghindari menggunakan beberapa task runner.

Saya dapat menyadarinya dengan menjalankan proses di latar belakang ( &), tetapi kemudian saya harus meletakkannya di latar depan untuk menghentikannya. Saya ingin memiliki proses untuk membungkus mereka dan ketika saya menghentikan proses itu menghentikan 'anak-anaknya'.

pengguna1876909
sumber
Haruskah mereka berjalan secara bersamaan atau satu demi satu?
Minix
Ya, proses harus berjalan secara bersamaan, tetapi saya perlu melihat output dari masing-masing.
user1876909

Jawaban:

67

Untuk menjalankan perintah secara bersamaan, Anda dapat menggunakan &pemisah perintah.

~$ command1 & command2 & command3

Ini akan mulai command1, lalu jalankan di latar belakang. Sama dengan command2. Kemudian itu dimulai secara command3normal.

Output dari semua perintah akan kacau, tetapi jika itu bukan masalah bagi Anda, itu akan menjadi solusinya.

Jika Anda ingin memiliki tampilan terpisah pada output nanti, Anda dapat menyalurkan output dari setiap perintah tee, yang memungkinkan Anda menentukan file untuk mencerminkan output.

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

Outputnya mungkin akan sangat berantakan. Untuk mengatasinya, Anda bisa memberikan output dari setiap perintah menggunakan awalan sed.

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

Jadi jika kita menyatukan semua itu kita dapatkan:

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3

Ini adalah versi yang sangat ideal tentang apa yang mungkin akan Anda lihat. Tapi itu yang terbaik yang bisa saya pikirkan saat ini.

Jika Anda ingin menghentikan semuanya sekaligus, Anda dapat menggunakan build in trap.

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

Ini akan mengeksekusi command1dan command2di latar belakang dan command3di latar depan, yang memungkinkan Anda membunuhnya dengan Ctrl+ C.

Ketika Anda membunuh proses terakhir dengan Ctrl+ Cyang kill %1; kill %2perintah dijalankan, karena kita terhubung eksekusi mereka dengan penerimaan dari interupsi sinyal, hal yang dikirim dengan menekan Ctrl+ C.

Mereka masing-masing membunuh proses latar belakang 1 dan 2 (Anda command1dan command2). Jangan lupa untuk menghapus jebakan, setelah Anda selesai dengan perintah Anda menggunakan trap - SIGINT.

Monster perintah yang lengkap:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'

Anda tentu saja dapat melihat layar . Ini memungkinkan Anda membagi konsol Anda menjadi konsol terpisah sebanyak yang Anda inginkan. Jadi Anda dapat memonitor semua perintah secara terpisah, tetapi pada saat yang sama.

Minix
sumber
3
Menantikan semua kritik. :)
Minix
2
Terima kasih. Itu tidak persis apa yang saya inginkan (perintah perangkap). Solusinya harus dibungkus dalam naskah untuk dapat digunakan kembali.
user1876909
@ user1876909 Ini kerangka. Spesifikasi terserah Anda [1]. Senang bisa membantu. [1] pastebin.com/jKhtwF40
Minix
3
ini adalah solusi yang agak cerdik, iv belajar sesuatu yang baru, bravo +1
mike-m
1
Ini luar biasa. Saya harus banyak menggali dan belajar dari jawaban ini.
ccnokes
20

Anda dapat dengan mudah membunuh banyak proses sekaligus jika Anda mengatur untuk menjalankannya (dan hanya mereka) dalam grup proses yang sama .

Linux menyediakan utilitas setsiduntuk menjalankan program dalam grup proses baru (bahkan dalam sesi baru, tetapi kami tidak peduli tentang itu). (Ini bisa dilakukan tetapi lebih rumit tanpa setsid.)

ID grup proses (PGID) grup proses adalah ID proses dari proses induk asli dalam grup. Untuk membunuh semua proses dalam grup proses, berikan negatif PGID ke killpanggilan sistem atau perintah. PGID tetap valid bahkan jika proses asli dengan PID ini mati (meskipun bisa sedikit membingungkan).

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"

Jika Anda menjalankan proses di latar belakang dari shell non-interaktif , maka mereka semua akan tetap berada di grup proses shell. Hanya di shell interaktif yang proses latar belakang berjalan di grup proses mereka sendiri. Karenanya, jika Anda melakukan fork perintah dari shell non-interaktif yang tetap berada di latar depan, Ctrl+ Cakan membunuh semuanya. Gunakan waitbuiltin untuk membuat shell menunggu semua perintah untuk keluar.

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all
Gilles 'SANGAT berhenti menjadi jahat'
sumber
2
jawaban tidak terdaftar (metode di bagian bawah). Mudah dimengerti dan dihafal.
Nicolas Marshall
14

Saya terkejut ini belum datang, tapi cara favorit saya untuk melakukan ini adalah dengan menggunakan subshell dengan perintah latar belakang. Pemakaian:

(command1 & command2 & command3)

Output terlihat dari keduanya, semua perintah bash dan kenyamanan masih tersedia, dan satu Ctrl-cmembunuh semuanya. Saya biasanya menggunakan ini untuk memulai server file klien live-debugging dan server backend pada saat yang sama, jadi saya bisa membunuh mereka berdua dengan mudah ketika saya mau.

Cara kerjanya adalah itu command1dan command2sedang berjalan di latar belakang instance subshell baru, dan command3berada di latar depan instance itu, dan subshell tersebut adalah yang pertama kali menerima sinyal kill dari Ctrl-cpenekanan tombol. Ini sangat mirip dengan menjalankan skrip bash sehubungan dengan siapa yang memiliki proses apa dan kapan program latar belakang terbunuh.

jv-dev
sumber
Jika Anda menambahkan waitsebagai perintah terakhir (command3 dalam contoh Anda), Anda dapat menjalankan kode pembersihan setelah pengguna menekan Ctrl-c.
dotnetCarpenter
0

Anda dapat menggunakan titik koma ;atau &&seperti ini:

cmd1; cmd2   # Runs both the commands, even if the first one exits with a non-zero status.

cmd1 && cmd2 # Only runs the second command if the first one was successful.
serenesat
sumber
Itu tidak menjalankan perintah secara bersamaan.
Gilles 'SANGAT berhenti menjadi jahat'
-2

Anda bisa menggunakan a pipe. Ini akan memulai perintah di kedua ujung pipa secara bersamaan. Anda harus dapat menghentikan semua perintah dengan CTRL-Cterlalu.

1st command | 2nd command | 3rd command

Jika Anda ingin menjalankan perintah satu demi satu, Anda dapat menggunakan metode @ serenesat.

Sree
sumber
Ini meneruskan output dari perintah pertama ke yang kedua (dan seterusnya), yang tidak sesuai di sini karena output dari perintah pertama seharusnya berakhir di terminal.
Gilles 'SANGAT berhenti menjadi jahat'