Bash'de alt kabuğu çağırmanın kuralı?


24

Bir subshell oluşturmak için Bash kuralını yanlış anlamış gibiyim. Parantezlerin daima kendi süreci olarak çalışan bir alt kabuk yarattığını düşündüm.

Ancak, durum böyle görünmüyor. Kod Parçacığı A'da (aşağıda), ikinci sleepkomut ( pstreebaşka bir terminalde belirlendiği gibi) ayrı bir kabukta çalışmaz . Ancak, Kod Parçacığı B'de, ikinci sleepkomut mu ayrı kabukta çalıştırın. Parçacıklar arasındaki tek fark, ikinci pasajın parantez içinde iki komut içermesidir.

Biri lütfen alt kabukların ne zaman oluşturulduğunun kuralını açıklayabilir mi?

KOD SNIPPET A:

sleep 5
(
sleep 5
)

KOD SNIPPET B:

sleep 5
(
x=1
sleep 5
)

Yanıtlar:


20

Parantezler her zaman bir alt kabuk başlatır. Olan şudur ki bash sleep 5, bu alt kabuk tarafından yürütülen en son komut olduğunu algılar , bu yüzden + execyerine çağırır . Komut aynı süreçte altkabuk değiştirir.forkexecsleep

Başka bir deyişle, temel durum:

  1. ( … )alt kabuk yarat. Orijinal işlem forkve çağırır wait. Bir alt kabuk olan alt işlemde:
    1. sleepalt işlemin bir alt işlemini gerektiren harici bir komuttur. Alt kabuk çağırır forkve wait. Alt işlemde:
      1. Alt işlem, harici komutu → yürütür exec.
      2. Sonunda komut → sona erer exit.
    2. wait deniz kabuğu içinde tamamlar.
  2. wait orijinal süreçte tamamlar.

Optimizasyon:

  1. ( … )alt kabuk yarat. Orijinal işlem forkve çağırır wait. Alt işlemde, çağrılana kadar alt kabuk olan exec:
    1. sleep harici bir komuttur ve bu işlemin yapması gereken son şeydir.
    2. Alt işlem harici komutu → yürütür exec.
    3. Sonunda komut → sona erer exit.
  2. wait orijinal süreçte tamamlar.

sleepÇağrıdan sonra başka bir şey eklediğinizde , alt kabuğun etrafta tutulması gerekir, bu nedenle bu optimizasyon gerçekleşemez.

Aramadan önce başka bir şey eklediğinizde sleep, optimizasyon yapılabilir (ve ksh yapar), ancak bash bunu yapmaz (bu optimizasyonda çok muhafazakar).


Subshell çağrılarak oluşturulur forkve alt süreç çağrılarak (harici komutları çalıştırmak için) yaratılırfork + exec . Ama ilk paragrafınız bunun fork + execdenizaltı için de çağrıldığını gösteriyor . Neyi yanlış yapıyorum?
hac,

1
@ haccks fork+ execalt kabuk için çağrılmaz, harici komut için çağrılır. Herhangi bir optimizasyon olmadan fork, alt kabuk ve harici komut için başka bir çağrı var . Cevabıma ayrıntılı bir akış açıklaması ekledim.
Gilles 'SO- kötü olmak'

Güncelleme için bir ton teşekkürler. Şimdi daha iyi açıklıyor. Ondan, (...)(temel durumda) durumunda exec, alt kabuğun yürütecek herhangi bir harici komutu olup olmadığına bağlı olarak, herhangi bir harici komutun yürütülmesi durumunda olması gerekip gerekmediğine bağlı olarak bir çağrı olabileceğini veya olmayabilir fork + exec.
hac

Bir soru daha: Bu optimizasyon sadece alt kabuk için işe yarıyor mu yoksa datebir kabuktaki gibi bir komut için yapılabilir mi?
hac

@ haccks Soruyu anlamıyorum. Bu optimizasyon, bir kabuk işleminin en son yaptığı gibi harici bir komut çağırmakla ilgilidir. Alt kabuklarla sınırlı değildir: karşılaştırın strace -f -e clone,execve,write bash -c 'date'vestrace -f -e clone,execve,write bash -c 'date; true'
Gilles 'KÖTÜ'

4

Gönderen İleri Bash Programlama Kılavuzu :

“Genel olarak, bir komut dosyasındaki harici bir komut, bir alt işlemi geçersiz kılar, buna karşın bir Bash yerleşik değil. Bu nedenle, yerleşikler daha hızlı çalışır ve harici komut eşdeğerlerinden daha az sistem kaynağı kullanır.”

Ve biraz daha aşağı:

"Parantez arasına gömülmüş bir komut listesi alt kabuk olarak çalışıyor."

Örnekler:

[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089

OPs kodunu kullanan örnek (daha sabırsız olduğum için daha kısa sürede uyuyor):

echo $BASHPID

sleep 2
(
    echo $BASHPID
    sleep 2
    echo $BASHPID
)

Çıktı:

[root@talara test]# bash sub_bash
6606
6608
6608

2
Cevabınız için teşekkürler Tim. Ancak sorumu tam olarak yanıtladığından emin değilim. "Parantezler arasına gömülü bir komut listesi bir alt kabuk olarak çalışıyor" dan beri, ikincisinin sleepbir alt kabukta çalışmasını beklerdim (belki de alt kabuğun bir alt işlemi yerine gömülü olduğundan, alt kabuğun işleminde). Bununla birlikte, herhangi bir durumda, bir alt kabuğun olmasını, yani ana Bash işlemi altındaki bir Bash alt işleminin olmasını beklerdim. Yukarıdaki Snippet B için durum böyle görünmüyor.
bashful

Düzeltme: Çünkü sleepbir yerleşik görünmüyor sleep, her iki snippet'teki ikinci aramanın subshell işleminin bir alt işleminde çalışmasını beklerdim .
bashful

@bashful $BASHPIDDeğişkenimle kodunuzu kırma özgürlüğünü kullandım . Ne yazık ki bunu yaptığın yol sana inandığım bütün hikayeyi vermek değildi. Cevabımdaki ek çıktıma bakın.
Tim

4

@Gilles cevaplarına ek bir not.

Gilles tarafından söylendiği gibi: The parentheses always start a subshell.

Ancak, bu alt kabuğun sahip olduğu rakamlar tekrar edebilir:

$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679

Gördüğünüz gibi, $$ tekrar ediyor ve bu beklendiği gibi, çünkü (doğru man bashsatırı bulmak için bu komutu yürütün ):

$ LESS=+/'^ *BASHPID' man bash

BASHPID
Geçerli bash işleminin işlem kimliğine genişler. Bu, bash'in yeniden başlatılmasını gerektirmeyen alt kabuklar gibi belirli durumlarda $$'den farklıdır.

Yani: Eğer kabuk yeniden başlatılmazsa, $$ aynıdır.

Veya bununla:

$ LESS=+/'^ *Special Parameters' man bash

Özel Parametreler
$ Kabuğun işlem kimliğine genişler. Bir () alt kabuğunda, alt kabuğa değil, geçerli kabuğun işlem kimliğine genişler.

$$Mevcut kabuk (değil alt kabuk) kimliğidir.


1
Belirli bir bölümde bash
manpage'i
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.