Bash pembuatan subkulit dengan kurung kurawal

31

Menurut ini , menempatkan daftar perintah antara kurung kurawal menyebabkan daftar dieksekusi dalam konteks shell saat ini. Tidak ada subkulit yang dibuat .

Menggunakan psuntuk melihat ini dalam aksi

Ini adalah hierarki proses untuk pipa proses yang dieksekusi langsung pada baris perintah. 4398 adalah PID untuk shell login:

sleep 2 | ps -H;
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29696 pts/23   00:00:00   sleep
   29697 pts/23   00:00:00   ps

Sekarang ikuti hierarki proses untuk pipa proses antara kurung kurawal yang dieksekusi langsung pada baris perintah. 4398 adalah PID untuk shell login. Ini mirip dengan hierarki di atas yang membuktikan bahwa semuanya dijalankan dalam konteks shell saat ini :

{ sleep 2 | ps -H; }
   PID TTY          TIME CMD
    4398 pts/23   00:00:00 bash
    29588 pts/23   00:00:00   sleep
    29589 pts/23   00:00:00   ps

Sekarang, ini adalah hierarki proses ketika sleeppipa itu sendiri ditempatkan di dalam kurung kurawal (jadi ada dua level kurung kurawal)

{ { sleep 2; } | ps -H; }
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29869 pts/23   00:00:00   bash
   29871 pts/23   00:00:00     sleep
   29870 pts/23   00:00:00   ps

Mengapa bashharus membuat subkulit untuk berjalan sleepdalam kasus ke-3 ketika dokumentasi menyatakan bahwa perintah antara kurung kurawal dieksekusi dalam konteks shell saat ini?

iruvar
sumber
Menarik, saya kira itu karena dalam kasus ke-3 grup dalam adalah bagian dari pipeline, dan karenanya dieksekusi dalam sub-shell misalnya, seperti pemanggilan fungsi lain yang merupakan bagian dari pipeline. Apakah masuk akal?
Miroslav Koškár
2
Saya tidak akan mengatakan "Shell harus" hanya karena itu ... Pipa tidak dieksekusi dalam konteks shell. Jika pipeline hanya terdiri dari perintah eksternal maka membuat subproses sudah cukup. { sleep 2 | command ps -H; }
Hauke ​​Laging

Jawaban:

26

Dalam sebuah pipeline, semua perintah berjalan secara bersamaan (dengan stdout / stdin dihubungkan oleh pipa) sehingga dalam proses yang berbeda.

Di

cmd1 | cmd2 | cmd3

Ketiga perintah dijalankan dalam proses yang berbeda, jadi setidaknya dua di antaranya harus dijalankan dalam proses anak. Beberapa shell menjalankan salah satu dari mereka dalam proses shell saat ini (jika builtin suka readatau jika pipeline adalah perintah terakhir dari skrip), tetapi bashmenjalankan semuanya dalam proses terpisah mereka sendiri (kecuali dengan lastpipeopsi di bashversi terbaru dan dalam beberapa kondisi tertentu ).

{...}perintah grup. Jika grup itu adalah bagian dari sebuah pipeline, ia harus dijalankan dalam proses terpisah seperti perintah sederhana.

Di:

{ a; b "$?"; } | c

Kami membutuhkan shell untuk mengevaluasi itu a; b "$?"adalah proses yang terpisah, jadi kami membutuhkan subkulit. Shell dapat dioptimalkan dengan tidak forking bkarena itu perintah terakhir yang dijalankan dalam grup itu. Beberapa kerang melakukannya, tetapi ternyata tidak bash.

Stéphane Chazelas
sumber
" Ketiga perintah dijalankan dalam proses yang berbeda, jadi setidaknya dua di antaranya harus dijalankan dalam sebuah subkulit. " Mengapa subkulit diperlukan dalam kasus ini? Bisakah shell induk tidak memunculkan proses pohon? Atau, Ketika Anda mengatakan " harus dijalankan dalam subkulit ", apakah maksud Anda shell akan bercabang sendiri, dan kemudian exec untuk setiap cmd1 cmd2 dan cmd3? Jika saya menjalankan ini, bash -c "sleep 112345 | cat | cat "saya hanya melihat satu bash dibuat dan kemudian 3 anak untuk itu tanpa sub bash lain interleaving.
Hakan Baba
" Kami membutuhkan shell untuk mengevaluasi bahwa; b" $? "Adalah proses yang terpisah, jadi kami membutuhkan subkulit. " Bisakah Anda juga memperluas alasannya? Mengapa kita perlu subkulit untuk memahaminya? Apa yang diperlukan untuk memahami itu? Saya kira parsing diperlukan tetapi apa lagi? . Bisakah shell induk tidak diuraikan a; b "$?"? Apakah benar-benar ada kebutuhan mendasar untuk sebuah subsheel, atau mungkin itu adalah keputusan desain / implementasi di bash?
Hakan Baba
@HakanBaba, saya telah mengubahnya menjadi "proses anak" untuk menghindari kemungkinan kebingungan.
Stéphane Chazelas
1
@HakanBaba, penguraian dilakukan pada orang tua (proses yang membaca kode, proses yang dilakukan penerjemah kecuali kode tersebut diteruskan eval), tetapi evaluasi (jalankan perintah pertama, tunggu, jalankan yang kedua) adalah dilakukan pada anak, yang stdout terhubung ke pipa.
Stéphane Chazelas
di { sleep 2 | ps -H; }bash induk melihat sleep 2yang membutuhkan garpu / exec. Tetapi dalam { { sleep 2; } | ps -H; }bash induk melihat { sleep 2; }dengan kata lain, beberapa kode bash. Sepertinya orang tua dapat menangani fork / exec untuk sleep 2tetapi memunculkan bash baru secara rekursif untuk menangani kode bash yang ditemui. Itu pemahaman saya, apakah masuk akal?
Hakan Baba
19

Bersarang di kurung kurawal akan menunjukkan bahwa Anda sedang membuat tingkat tambahan pelingkupan yang membutuhkan sub-shell baru untuk dipanggil. Anda dapat melihat efek ini dengan salinan Bash ke-2 di ps -Houtput Anda .

Hanya proses yang ditetapkan pada kurung kurawal tingkat pertama yang dijalankan dalam ruang lingkup Bash shell asli. Setiap kurung kurawal bersarang akan berjalan di cangkang Bash miliknya sendiri.

Contoh

$ { { { sleep 20; } | sleep 20; } | ps -H; }
  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5012 pts/1    00:00:00   bash
 5014 pts/1    00:00:00     bash
 5016 pts/1    00:00:00       sleep
 5015 pts/1    00:00:00     sleep
 5013 pts/1    00:00:00   ps

Mengambil | ps -Hkeluar dari campuran supaya kita bisa melihat kurung kurawal bersarang, kita bisa lari ps auxf | lessdi shell lain.

saml     29190  0.0  0.0 117056  3004 pts/1    Ss   13:39   0:00  \_ bash
saml      5191  0.0  0.0 117056  2336 pts/1    S+   14:42   0:00  |   \_ bash
saml      5193  0.0  0.0 107892   512 pts/1    S+   14:42   0:00  |   |   \_ sleep 20
saml      5192  0.0  0.0 107892   508 pts/1    S+   14:42   0:00  |   \_ sleep 20
saml      5068  0.2  0.0 116824  3416 pts/6    Ss   14:42   0:00  \_ bash
saml      5195  0.0  0.0 115020  1272 pts/6    R+   14:42   0:00      \_ ps auxf
saml      5196  0.0  0.0 110244   880 pts/6    S+   14:42   0:00      \_ less

Tapi tunggu, masih ada lagi!

Jika Anda mengambil pipa dan menggunakan bentuk perintah ini, kami melihat apa yang sebenarnya Anda harapkan:

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"

Sekarang di jendela arloji yang dihasilkan, kami mendapatkan pembaruan setiap 2 detik dari apa yang terjadi:

Inilah yang pertama sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5678 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5681 pts/1    00:00:00     watch
 5682 pts/1    00:00:00       ps

Inilah yang kedua sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5691 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5694 pts/1    00:00:00     watch
 5695 pts/1    00:00:00       ps

Inilah yang ketiga sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5704 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5710 pts/1    00:00:00     watch
 5711 pts/1    00:00:00       ps

Perhatikan ketiga tidur meskipun dilakukan pada tingkat yang berbeda dari kurung kurawal tetap pada PID 5676 dari Bash. Jadi saya percaya masalah Anda disebabkan oleh penggunaan | ps -H.

Kesimpulan

Penggunaan | ps -H(yaitu pipa) menyebabkan sub-shell tambahan, jadi jangan gunakan metode itu ketika mencoba untuk menginterogasi apa yang terjadi.

slm
sumber
jadi, "Hanya proses yang ditetapkan pada kurung kurawal tingkat pertama yang dijalankan dalam ruang lingkup Bash shell asli."
xealits
@xealits - apakah itu tindak lanjut T yang Anda tanyakan kepada saya?
slm
@slm itu hanya penekanan pada poin utama dari jawaban, seperti yang saya lihat. Perintah di tingkat pertama kurung kurawal berjalan di shell saat ini, kurung kurawal bersarang membuat kerang baru. Kurung berbeda dalam membuat subkulit segera, di tingkat pertama. Jika saya salah - perbaiki saya. Tapi, seperti yang ditunjukkan orang lain, pertanyaan awal juga memiliki pipa. Demikianlah penciptaan proses yang terpisah. Dan kurung kurawal harus membuat shell ketika digunakan untuk proses terpisah. Jadi, mungkin, itulah alasan perilaku tersebut.
xealits
sekarang, setelah membaca ulang posting Anda, saya melihat saya salah - pernyataan bersarang hanya menyangkut kasus pipa. Jadi, kurung kurawal jangan pernah membuat kerang baru, kecuali jika Anda membungkus proses terpisah dengan mereka - maka mereka harus.
xealits
@xealits - itu benar.
slm
7

Saya akan memposting hasil tes saya, yang membawa saya pada kesimpulan bahwa bash membuat sub-shell untuk perintah grup jika dan hanya jika itu adalah bagian dari pipeline, itu sama seperti jika seseorang akan memanggil beberapa fungsi yang juga akan dipanggil di sub-shell.

$ { A=1; { A=2; sleep 2; } ; echo $A; }
2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1
Miroslav Koškár
sumber
A saya juga menunjukkan ini.
slm