Potrzebujesz wyjaśnień dotyczących zachowania polecenia Exec wbudowanego w Linux bash

Z podręcznika referencyjnego Bash otrzymuję następujące informacje o exec wbudowanym poleceniu bash:

Jeśli podano polecenie, zastępuje ono powłokę bez tworzenia nowego procesu.

Teraz mam następujący bash skrypt:

#!/bin/bash
exec ls;
echo 123;
exit 0

This, I got this:

cleanup.sh  ex1.bash  file.bash  file.bash~  output.log
(files from the current directory)

Teraz, jeśli mam ten skrypt:

#!/bin/bash
exec ls | cat
echo 123
exit 0

Otrzymuję następujące wyjście:

cleanup.sh
ex1.bash
file.bash
file.bash~
output.log
123

Moje pytanie brzmi:

Jeśli wywołanie exec zastępuje powłoka bez tworzenia nowego procesu , dlaczego po umieszczeniu | cat, echo 123 jest drukowana, ale bez niej nie jest. więc byłbym szczęśliwy, gdyby ktoś mógł wyjaśnić, jaka jest logika tego zachowania.

Dzięki.

Edytuj: Po odpowiedzi @torek dostaję jeszcze trudniejsze do wytłumaczenia zachowanie:

1.exec ls>out polecenie tworzy plik out i umieszcza w nim wynik polecenia ls;

2.exec ls>out1 ls>out2 tworzy tylko pliki, ale nie umieszcza wewnątrz żadnego wyniku. Jeśli polecenie działa zgodnie z sugestią, wydaje mi się, że polecenie numer 2 powinno mieć taki sam wynik jak polecenie numer 1 (Co więcej, myślę, że nie powinno było tworzyć pliku out2).

Author: Denilson Sá Maia, 2012-03-29

1 answers

W tym konkretnym przypadku, masz exec w rurociągu. Aby wykonać serię poleceń rurociągu, powłoka musi początkowo rozwidlać się, tworząc pod-powłokę. (W szczególności musi stworzyć rurę, a następnie widelec, aby wszystko działało "po lewej" rury może mieć swoje wyjście wysłane do tego, co jest "po prawej" rury.)

Aby zobaczyć, że tak naprawdę się dzieje, porównaj:

{ ls; echo this too; } | cat

Z:

{ exec ls; echo this too; } | cat

Pierwszy biegnie ls bez wychodzenia z sub-shell, tak, że ta sub-shell jest więc nadal w pobliżu, aby uruchomić echo. Ta ostatnia uruchamia ls, opuszczając sub-powłokę, która w związku z tym nie jest już dostępna do wykonania echo, a this too nie jest drukowana.

(użycie nawiasów klamrowych { cmd1; cmd2; } zwykle tłumi działanie widelca pod powłoki, które otrzymuje się za pomocą nawiasów (cmd1; cmd2), ale w przypadku rury, widelec jest jakby "wymuszony".)

Przekierowanie aktualnej powłoki dzieje się tylko wtedy, gdy nie ma "nic do uruchomienia", jak to było, po słowie exec. W ten sposób np. exec >stdout 4<input 5>>append modyfikuje obecną powłokę, ale exec foo >stdout 4<input 5>>append próbuje wykonać polecenie foo. [Uwaga: nie jest to ściśle dokładne; patrz dodatek.]

Co ciekawe, w interaktywnej powłoce, po awarii exec foo >output, ponieważ nie ma komendy foo, powłoka pozostaje w pobliżu, ale stdout pozostaje przekierowany do pliku output. (Możesz odzyskać z exec >/dev/tty. W skrypcie brak exec foo kończy działanie skryptu.)


Z czubkiem kapelusza do @ Pumbaa80, oto coś jeszcze bardziej ilustrującego:
#! /bin/bash
shopt -s execfail
exec ls | cat -E
echo this goes to stdout
echo this goes to stderr 1>&2

(Uwaga: cat -E jest uproszczona w dół od mojego zwykłego cat -vET, który jest moim przydatnym rozwiązaniem dla "pozwól mi zobaczyć niedrukowalne znaki w rozpoznawalny sposób"). Gdy skrypt jest uruchomiony, wyjście z ls ma cat -E zastosowane (w Linuksie powoduje to, że koniec linii jest widoczny jako znak$), ale wyjście wysyłane do stdout i stderr (na pozostałych dwóch liniach) jest przekierowywane , a nie. Zmień | cat -E na > out i po uruchomieniu skryptu obserwuj zawartość plik out: dwóch ostatnich echo s nie ma tam.

Teraz zmień ls na foo (lub inne polecenie, które nie zostanie znalezione) i uruchom skrypt ponownie. Tym razem Wyjście To:

$ ./demo.sh
./demo.sh: line 3: exec: foo: not found
this goes to stderr

I plik out ma teraz zawartość wytworzoną przez pierwszą linię echo.

To sprawia, że to, co exec "naprawdę robi", jest tak oczywiste, jak to tylko możliwe (ale nie bardziej oczywiste, jak Albert Einstein tego nie ujął :-) ).

Normalnie, gdy powłoka wykonuje " proste polecenie" (dokładna definicja znajduje się na stronie podręcznika ekranowego, ale w szczególności wyklucza to polecenia w "potoku"), przygotowuje wszelkie operacje przekierowania We/Wy określone <, >, i tak dalej, otwierając potrzebne pliki. Następnie powłoka wywołuje fork (lub jakiś równoważny, ale bardziej wydajny wariant, taki jak vfork lub clone w zależności od bazowego systemu operacyjnego, konfiguracji itp.), a w procesie potomnym przestawia deskryptory otwartych plików (używając wywołań dup2 lub równoważnych), aby osiągnąć pożądany ostateczne ustalenia: > out przenosi otwarty deskryptor do FD 1-stdout-podczas gdy 6> out przenosi otwarty deskryptor do fd 6.

Jeśli podasz słowo kluczowe exec, powłoka będzie tłumić krok fork. Robi to jak zwykle otwieranie i zmienianie deskryptora pliku, ale tym razem, wpływa na wszystkie kolejne polecenia . W końcu, po wykonaniu wszystkich przekierowań, powłoka próbuje execve() (w sensie wywołania systemowego) wykonać polecenie, jeśli takie istnieje. Jeśli nie ma polecenie, lub jeśli wywołanie execve() nie powiedzie się i powłoka ma kontynuować działanie( jest interaktywna lub masz ustawione execfail), powłoka jest włączona. Jeśli execve() powiedzie się, powłoka przestaje istnieć, po zastąpieniu jej nowym poleceniem. Jeśli execfail jest wyłączona i powłoka nie jest interaktywna, powłoka kończy działanie.

(istnieje również komplikacja command_not_found_handle funkcji powłoki: bash exec wydaje się tłumić uruchamianie go, na podstawie wyników testów. Słowo kluczowe exec w general sprawia, że powłoka nie patrzy na własne funkcje, tzn. jeśli masz funkcję powłoki f, uruchamiając f jako proste polecenie uruchamia funkcję powłoki, podobnie jak (f), która uruchamia ją w pod-powłoce, ale uruchamiając (exec f) przeskakuje ją.)


Jeśli chodzi o to, dlaczego ls>out1 ls>out2 tworzy dwa pliki (z exec lub bez), Jest to dość proste: powłoka otwiera każde przekierowanie, a następnie używa dup2 do przenoszenia deskryptorów plików. Jeśli masz dwa zwykłe przekierowania >, powłoka otwiera oba, przesuwa pierwszy do fd 1( stdout), a nastÄ ™ pnie przesuwa drugi do fd 1 (stdout ponownie), zamykajÄ ... c pierwszy w procesie. W końcu działa ls ls, ponieważ to zostało po usunięciu >out1 >out2. Tak długo, jak nie ma pliku o nazwie ls, polecenie ls skarży się na stderr i nie zapisze nic na stdout.
 38
Author: torek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-02 08:59:03