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?


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.


„ 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.


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

Mój A też to pokazuje.
— slm
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.