Tworzenie podpowłoki za pomocą nawiasów klamrowych

31

Zgodnie z tym , umieszczenie listy poleceń między nawiasami klamrowymi powoduje, że lista jest wykonywana w bieżącym kontekście powłoki. Nie jest tworzona podpowłoka .

Za pomocą, psaby zobaczyć to w akcji

Jest to hierarchia procesów dla potoku procesu wykonywanego bezpośrednio w wierszu poleceń. 4398 to PID dla powłoki logowania:

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

Teraz postępuje zgodnie z hierarchią procesu dla potoku procesu między nawiasami klamrowymi wykonywanymi bezpośrednio w wierszu poleceń. 4398 to PID dla powłoki logowania. Jest to podobne do powyższej hierarchii udowadniającej, że wszystko jest wykonywane w bieżącym kontekście powłoki :

{ 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

Jest to hierarchia procesów, sleepw której potok jest umieszczony wewnątrz nawiasów klamrowych (czyli w sumie dwa poziomy nawiasów klamrowych)

{ { 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

Dlaczego bashtrzeba utworzyć podpowłokę, aby działała sleepw trzecim przypadku, gdy dokumentacja stwierdza, że ​​polecenia między nawiasami klamrowymi są wykonywane w bieżącym kontekście powłoki?

iruvar
źródło
Interesujące, zgaduję, że dzieje się tak, ponieważ w trzecim przypadku grupa wewnętrzna jest częścią potoku, a zatem jest wykonywana w podpowłoce, np. Jak każde inne wywołanie funkcji, które byłoby częścią potoku. Czy jest sens?
Miroslav Koškár
2
Nie powiedziałbym „Powłoka musi” tylko dlatego, że tak… Rurociągi nie są wykonywane w kontekście powłoki. Jeśli potok składa się wyłącznie z zewnętrznych poleceń, wystarczy utworzenie podprocesów. { sleep 2 | command ps -H; }
Hauke ​​Laging

Odpowiedzi:

26

W potoku wszystkie polecenia są uruchamiane jednocześnie (z ich stdout / stdin połączonymi rurami), a więc w różnych procesach.

W

cmd1 | cmd2 | cmd3

Wszystkie trzy polecenia działają w różnych procesach, więc co najmniej dwa z nich muszą działać w procesie potomnym. Niektóre powłoki uruchamiają jedną z nich w bieżącym procesie powłoki (jeśli są wbudowane readlub jeśli potok jest ostatnim poleceniem skryptu), ale bashwszystkie działają w osobnym procesie (z wyjątkiem lastpipeopcji w najnowszych bashwersjach i pod pewnymi warunkami ).

{...}grupuje polecenia. Jeśli ta grupa jest częścią potoku, musi działać w osobnym procesie, podobnie jak proste polecenie.

W:

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

Potrzebujemy powłoki, aby ocenić, że a; b "$?"jest to osobny proces, dlatego potrzebujemy podpowłoki. Powłoka mogłaby zoptymalizować nie buruchamiając forka, ponieważ jest to ostatnie polecenie, które można uruchomić w tej grupie. Niektóre pociski to robią, ale najwyraźniej nie bash.

Stéphane Chazelas
źródło
Wszystkie trzy polecenia działają w różnych procesach, więc co najmniej dwa z nich muszą działać w podpowłoce. ” Dlaczego w takim przypadku podpowłoka jest konieczna? Czy powłoka nadrzędna nie może spawnować procesów drzewa? Lub kiedy mówisz „ musisz uruchomić w podpowłoce ”, masz na myśli, że powłoka rozwidli się sama, a następnie wykona dla każdego cmd1 cmd2 i cmd3? Jeśli wykonam to bash -c "sleep 112345 | cat | cat ", zobaczę, że utworzono tylko jedną bitwę, a następnie 3 dzieci bez żadnych innych przeplatających basów.
Hakan Baba
Potrzebujemy powłoki, aby ocenić, że a; b„ $? ”Jest osobnym procesem, więc potrzebujemy podpowłoki. ” Czy mógłbyś również rozszerzyć rozumowanie? Dlaczego potrzebujemy podsystemu, aby to zrozumieć? Co trzeba zrobić, aby to zrozumieć? Zakładam, że parsowanie jest wymagane, ale co jeszcze? . Czy powłoka nadrzędna nie może parsować a; b "$?"? Czy naprawdę istnieje podstawowa potrzeba na subheel, czy może jest to decyzja / implementacja projektu na bash?
Hakan Baba
@HakanBaba, zmieniłem to na „proces potomny”, aby uniknąć potencjalnego zamieszania.
Stéphane Chazelas
1
@HakanBaba, parsowanie odbywa się w obiekcie nadrzędnym (proces, który odczytuje kod, ten, który wykonał interpreter, chyba że ten kod został przekazany eval), ale ocena (uruchom pierwsze polecenie, poczekaj na niego, uruchom drugie) to zrobione w dziecku, które ma stdout podłączony do rury.
Stéphane Chazelas
w { sleep 2 | ps -H; }nadrzędnej wersji bash, sleep 2która wymaga fork / exec. Ale w { { sleep 2; } | ps -H; }bashu nadrzędnym widać { sleep 2; }innymi słowy, jakiś kod bash. Wygląda na to, że rodzic może obsłużyć fork / exec, sleep 2ale odradza nowe bash rekurencyjnie, aby obsłużyć napotkany kod bash. Tak rozumiem, czy to ma sens?
Hakan Baba
19

Zagnieżdżanie nawiasów klamrowych wydaje się oznaczać, że tworzysz dodatkowy poziom zakresu, który wymaga wywołania nowej podpowłoki. Możesz zobaczyć ten efekt z drugą kopią Bash w twoich ps -Hwynikach.

Tylko procesy określone na pierwszym poziomie nawiasów klamrowych są uruchamiane w zakresie oryginalnej powłoki Bash. Wszelkie zagnieżdżone nawiasy klamrowe będą działały we własnej skorupie Bash o określonym zasięgu.

Przykład

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

Po | ps -Husunięciu mieszanki, abyśmy mogli zobaczyć zagnieżdżone nawiasy klamrowe, możemy uruchomić ps auxf | lessinną powłokę.

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

Ale czekaj, jest więcej!

Jeśli jednak wyjmiesz rury i użyjesz tej formy polecenia, zobaczymy, czego się właściwie spodziewałbyś:

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

Teraz w wynikowym oknie zegarka otrzymujemy aktualizację co 2 sekundy tego, co się dzieje:

Oto pierwszy 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

Oto drugi 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

Oto trzeci 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

Zauważ, że wszystkie trzy sny, chociaż wywoływane na różnych poziomach zagnieżdżenia nawiasów klamrowych, faktycznie pozostają w obrębie PID 5676 Bash. Uważam więc, że Twój problem został spowodowany przez samego użytkownika | ps -H.

Wnioski

Użycie | ps -H(tj. Potoku) powoduje dodatkową podpowłokę, więc nie używaj tej metody, gdy próbujesz przesłuchać, co się dzieje.

slm
źródło
dlatego „Tylko procesy określone na pierwszym poziomie nawiasów klamrowych są uruchamiane w ramach oryginalnej powłoki Bash”.
xealits
@xealits - czy to pytanie, o które pytasz?
slm
@slm to tylko nacisk na główny punkt odpowiedzi, tak jak ją widziałem. Polecenia na pierwszym poziomie nawiasów klamrowych działają w bieżącej powłoce, zagnieżdżone nawiasy klamrowe tworzą nowe powłoki. Nawiasy różnią się od razu tworzeniem podpowłoki, na pierwszym poziomie. Jeśli popełniłem błąd - popraw mnie. Ale, jak podkreślają inni, początkowe pytanie ma również rurociągi. Zatem tworzenie oddzielnych procesów. A nawiasy klamrowe muszą tworzyć powłokę, gdy są używane w osobnym procesie. Prawdopodobnie jest to przyczyną tego zachowania.
xealits
teraz, po ponownym przeczytaniu twojego postu, widzę, że się myliłem - instrukcja zagnieżdżenia dotyczy tylko przypadku potoków. Tak więc nawiasy klamrowe nigdy nie tworzą nowych powłok, chyba że otoczysz je osobnym procesem - wtedy muszą.
xealits
@xealits - to prawda.
slm
7

Zamieszczę wyniki moich testów, co doprowadziło mnie do wniosku, że bash tworzy podpowłokę dla polecenia grupy, i tylko wtedy, gdy jest częścią potoku, to jest tak, jakby ktoś wywołał jakąś funkcję, która byłaby również nazwana w podpowłoce.

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

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1
Miroslav Koškár
źródło
Mój A też to pokazuje.
slm