Kiedy uruchamiam ten skrypt, zamierzam go uruchomić aż do zabicia ...
# foo.sh
while true; do sleep 1; done
... Nie mogę go znaleźć za pomocą ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... ale jeśli po prostu dodam wspólny #!
nagłówek " " do skryptu ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... wtedy skrypt można znaleźć za pomocą tego samego ps
polecenia ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Dlaczego tak jest?
To może być powiązane pytanie: Myślałem, że „ #
” to tylko przedrostek komentarza, a jeśli tak, to „ #! /usr/bin/bash
” sam w sobie jest jedynie komentarzem. Ale czy „ #!
” ma jakieś znaczenie większe niż tylko komentarz?
shell-script
shell
ps
shebang
StoneThrow
źródło
źródło
Odpowiedzi:
Gdy bieżąca interaktywna powłoka jest
bash
uruchomiona, a ty uruchomisz skrypt bez#!
-line, wtedybash
uruchom skrypt. Proces pojawi się naps ax
wyjściu jako sprawiedliwybash
.W innym terminalu:
Związane z:
Odpowiednie sekcje tworzą
bash
instrukcję:Oznacza to, że uruchamianie
./foo.sh
w wierszu poleceń, gdyfoo.sh
nie ma#!
-linii, jest takie samo, jak uruchamianie poleceń w pliku w podpowłoce, tj.Przy odpowiedniej linii
#!
wskazującej na np/bin/bash
. Robi to tak samoźródło
ps
skrypt działa jako „/usr/bin/bash ./foo.sh
”. Więc w pierwszym przypadku, jak mówisz, bash uruchomi skrypt, ale czy ten skrypt nie musiałby zostać „przekazany” do pliku wykonywalnego bash rozwidlonego, jak w drugim przypadku? (a jeśli tak, to wyobrażam sobie, że można go znaleźć przy pomocy fajki do grep ...?)$$
nadal wskazuje na stary proces w przypadku podpowłoki (echo $BASHPID
/bash -c 'echo $PPID'
).Kiedy zaczyna się skrypt powłoki
#!
, ten pierwszy wiersz jest komentarzem w odniesieniu do powłoki. Jednak pierwsze dwa znaki mają znaczenie dla innej części systemu: jądra. Dwie postacie#!
nazywane są shebang . Aby zrozumieć rolę shebang, musisz zrozumieć, jak program jest wykonywany.Wykonywanie programu z pliku wymaga działania z jądra. Odbywa się to w ramach
execve
wywołania systemowego. Jądro musi zweryfikować uprawnienia do pliku, zwolnić zasoby (pamięć itp.) Związane z plikiem wykonywalnym aktualnie uruchomionym w procesie wywoływania, przydzielić zasoby dla nowego pliku wykonywalnego i przenieść kontrolę do nowego programu (i więcej rzeczy, które Nie wspomnę).execve
Wywołanie systemowe zastępuje kod aktualnie uruchomionego procesu; istnieje osobne wywołanie systemowe,fork
aby utworzyć nowy proces.Aby to zrobić, jądro musi obsługiwać format pliku wykonywalnego. Ten plik musi zawierać kod maszynowy zorganizowany w sposób zrozumiały dla jądra. Skrypt powłoki nie zawiera kodu maszynowego, więc nie można go wykonać w ten sposób.
Mechanizm shebang pozwala jądru na odroczenie zadania interpretacji kodu do innego programu. Kiedy jądro widzi, że zaczyna się plik wykonywalny
#!
, odczytuje kilka kolejnych znaków i interpretuje pierwszy wiersz pliku (minus#!
spację wiodącą i opcjonalną) jako ścieżkę do innego pliku (plus argumenty, których nie będę tutaj omawiać ). Kiedy jądro ma wykonać plik/my/script
i widzi, że plik zaczyna się od linii#!/some/interpreter
, jądro wykonuje się/some/interpreter
z argumentem/my/script
. Następnie/some/interpreter
należy zdecydować, że/my/script
jest to plik skryptu, który powinien wykonać.Co jeśli plik nie zawiera natywnego kodu w formacie zrozumiałym dla jądra i nie zaczyna się od shebang? Cóż, wtedy plik nie jest wykonywalny, a
execve
wywołanie systemowe kończy się niepowodzeniem z kodem błęduENOEXEC
(błąd formatu wykonywalnego).To może być koniec historii, ale większość pocisków implementuje funkcję awaryjną. Jeśli jądro powróci
ENOEXEC
, powłoka sprawdza zawartość pliku i sprawdza, czy wygląda jak skrypt powłoki. Jeśli powłoka uważa, że plik wygląda jak skrypt powłoki, wykonuje go samodzielnie. Szczegóły tego, jak to robi, zależą od powłoki. Możesz zobaczyć, co się dzieje, dodającps $$
do skryptu, a więcej, obserwując proces, wstrace -p1234 -f -eprocess
którym 1234 to PID powłoki.W bash ten mechanizm rezerwowy jest realizowany przez wywołanie,
fork
ale nieexecve
. Proces potomnego bash sam usuwa stan wewnętrzny i otwiera nowy plik skryptu, aby go uruchomić. Dlatego proces, który uruchamia skrypt, nadal używa oryginalnego obrazu kodu bash, a oryginalne argumenty wiersza poleceń przekazane przy pierwszym wywołaniu bash. ATT ksh zachowuje się w ten sam sposób.Dash natomiast reaguje na
ENOEXEC
wywołanie/bin/sh
ze ścieżką do skryptu przekazaną jako argument. Innymi słowy, wykonanie skryptu bez kreski z kreski zachowuje się tak, jakby skrypt zawierał linię shebang#!/bin/sh
. Mksh i zsh zachowują się w ten sam sposób.źródło
bash
jest rozwidlone, ma dostęp do tej samejargv[]
tablicy, co jego rodzic, dzięki czemu wie, że „oryginalne argumenty wiersza poleceń zostały przekazane przy pierwszym wywołaniu bash”, i jeśli dlatego właśnie dziecku nie przekazano oryginalnego skryptu jako jawnego argumentu (a więc dlaczego grep nie może go znaleźć) - czy to jest dokładne?BINFMT_SCRIPT
moduł to kontroluje i może być usunięte / zmodularyzowane, chociaż zwykle jest statycznie połączone z jądrem), ale nie rozumiem, dlaczego byś chciał, chyba że w systemie osadzonym . Aby obejść tę możliwość,bash
ma flagę konfiguracji (HAVE_HASH_BANG_EXEC
) w celu kompensacji!ps
raporty jako argumenty wiersza poleceń, ale tylko do pewnego momentu: musi zmodyfikować istniejący bufor pamięci, nie może go powiększyć. Więc jeśli bash spróbuje zmodyfikować go,argv
aby dodać nazwę skryptu, nie zawsze będzie działać. Dziecko nie „przekazało argumentu”, ponieważ nigdy nie maexecve
wywołania systemowego w nim. To tylko ten sam obraz procesu bash, który działa.W pierwszym przypadku skrypt jest uruchamiany przez rozwidlone dziecko z bieżącej powłoki.
Najpierw powinieneś uruchomić,
echo $$
a następnie spojrzeć na powłokę, która ma identyfikator procesu powłoki jako identyfikator procesu nadrzędnego.źródło