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