Czy shebang określa powłokę uruchamiającą skrypt?

84

To może być głupie pytanie, ale wciąż je zadaję. Jeśli zadeklarowałem shebang

#!/bin/bash 

na początku my_shell_script.sh, więc zawsze muszę wywoływać ten skrypt za pomocą bash

[my@comp]$bash my_shell_script.sh

czy mogę użyć np

[my@comp]$sh my_shell_script.sh

a mój skrypt określa działającą powłokę za pomocą shebang? Czy to samo dzieje się ze kshskorupą? Korzystam z systemu AIX.

jrara
źródło
6
z twojej strony jest trochę zamieszania: kiedy zrobisz „_some_shell some_script”, zaczyna on _some_shell i prosi go o interpretację some_script. Więc nie, jeśli zrobisz „sh my_shell_script.sh”, to nie zinterpretuje shebang, ale zamiast tego zinterpretuje skrypt w sh. Aby użyć shebang: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Olivier Dulac

Odpowiedzi:

117

Shebang #! jest czytelny dla człowieka wystąpienie magicznym numerem składającym się z łańcucha bajtów 0x23 0x21, który jest wykorzystywany przez exec()rodzinę funkcji, aby określić, czy plik należy wykonać jest skrypt lub binarnym. Gdy shebang jest obecny, exec()zamiast tego uruchomi plik wykonywalny określony po shebang.

Zauważ, że oznacza to, że jeśli wywołasz skrypt, podając interpreter w wierszu poleceń, jak ma to miejsce w obu przypadkach podanych w pytaniu, exec()wykona on interpreter określony w wierszu poleceń, nawet nie spojrzy na skrypt.

Tak więc, jak zauważyli inni, jeśli chcesz exec()wywołać interpreter określony w linii shebang, skrypt musi mieć ustawiony bit wykonywalny i wywoływany jako ./my_shell_script.sh.

Zachowanie można łatwo wykazać za pomocą następującego skryptu:

#!/bin/ksh
readlink /proc/$$/exe

Wyjaśnienie:

  • #!/bin/kshdefiniuje kshsię jako tłumacz ustny.

  • $$ przechowuje PID bieżącego procesu.

  • /proc/pid/exe jest dowiązaniem symbolicznym do pliku wykonywalnego procesu (przynajmniej w Linuksie; w systemie AIX /proc/$$/object/a.out jest linkiem do pliku wykonywalnego).

  • readlink wyświetli wartość dowiązania symbolicznego.

Przykład:

Uwaga : Mam demonstrując to na Ubuntu, gdzie domyślna powłoka /bin/shjest dowiązaniem do kreska IE /bin/dashi /bin/kshjest dowiązaniem do /etc/alternatives/ksh, co z kolei jest dowiązaniem do /bin/pdksh.

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash
Thomas Nyman
źródło
dziękuję Thomasowi za tę odpowiedź. Udawaj, że uruchamiamy skrypt jako proces potomny z Node.js, Java lub cokolwiek innego. Czy możemy uruchomić proces „exec”, a następnie exec uruchomi skrypt powłoki? Pytam, bo szukam odpowiedzi na to pytanie: stackoverflow.com/questions/41067872/...
Alexander Mills
1
@AlexanderMills Przywołane exec()w tej odpowiedzi jest wywołanie systemowe, polecenie execto wbudowana powłoka, dlatego nie można wywołać exec programu z Node.js lub Java. Jednak każde polecenie powłoki wywołane np. Runtime.exec()W Javie jest ostatecznie przetwarzane przez exec()wywołanie systemowe.
Thomas Nyman,
Huh, tak, rzeczywiście znam interfejs API Java, o którym właśnie wspomniałeś. Zastanawiam się, czy istnieje sposób na wywołanie wywołania exec () niższego poziomu z Node.js
Alexander Mills,
@AlexanderMills Wyobrażam sobie, że child_process.{exec(),execFile(),spawn()} wszystko byłoby zaimplementowane przy użyciu C exec()(poprzez process).
Thomas Nyman,
10

Tak. Nawiasem mówiąc, nie jest to głupie pytanie. Odniesienie do mojej odpowiedzi jest tutaj . Uruchamianie skryptu za pomocą #!

  • Nazywa się to shebang lub „hukiem”.

  • To nic innego jak absolutna ścieżka do tłumacza Bash.

  • Składa się ze znaku liczbowego i znaku wykrzyknika (#!), Po którym następuje pełna ścieżka do interpretera, takiego jak / bin / bash.

    Wszystkie skrypty w Linuksie są uruchamiane przy użyciu interpretera określonego w pierwszym wierszu Prawie wszystkie skrypty bash często zaczynają się od #! / Bin / bash (przy założeniu, że Bash został zainstalowany w / bin) To gwarantuje, że Bash będzie używany do interpretacji skryptu, nawet jeśli jest wykonywany pod inną powłoką. Shebang został wprowadzony przez Dennisa Ritchiego między wersją 7 Unix a 8 w Bell Laboratories. Następnie został również dodany do linii BSD w Berkeley.

Ignoring An Interpreter Line (shebang)

Jeśli nie określisz linii interpretera, domyślnie jest to / bin / sh. Zalecane jest jednak ustawienie linii #! / Bin / bash.

vfbsilva
źródło
3
Aby to rozwinąć, jądro wie tylko, jak wykonywać statycznie połączone pliki binarne i gdzie znaleźć informacje o tłumaczu dla innych (specjalne pole w pliku binarnym lub linii shebang). Zazwyczaj wykonywanie skryptu powłoki oznacza podążanie za linią shebang do powłoki, a następnie podążanie za polem DT_INTERP w pliku binarnym powłoki do dynamicznego linkera.
Simon Richter,
5
Zauważ też, że nie ogranicza się to do skryptów powłoki. Wszystkie pliki skryptowe oparte na tekście wykorzystują to. np. #!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyInnym powszechnym wpisem shebang używanym do obsługi wielu systemów jest użycie env do zlokalizowania interpretera, którego chcesz użyć, na przykład#!/usr/bin/env perl #!/usr/bin/env python
sambler
Mówiąc o @sambler env, które z nich powinny być preferowane? Python i Perl często używają env, podczas gdy w skryptach powłoki jest to często pomijane, a shebang wskazuje na daną powłokę.
polemon
1
@polemon, którego mniej jest preferowane, a bardziej, które ścieżki się różnią. Podstawowe powłoki znajdują się na tej samej ścieżce we wszystkich systemach. Aktualne wersje Perla i Pythona mogą być instalowane w różnych lokalizacjach w różnych systemach, więc użycie env pozwala na zawsze działanie tego samego shebang, dlatego env jest częściej używany ze skryptami perl i python niż ze skryptami powłoki.
sambler
envznalezienie programu w $ PATH to trochę hack. Nie ustawia zmiennych środowiskowych, jak sugeruje nazwa. $ PATH może być innym rezultatem dla różnych użytkowników. Ale pomaga skryptom uruchamiać się bez modyfikacji w systemach, które umieszczają rozsądny interpreter perla w dziwnym miejscu.
John Mahowald
4

execWywołanie systemowe jądra Linux rozumie shebangs ( #!) natywnie

Kiedy robisz na bash:

./something

w Linuksie wywołuje to wywołanie execsystemowe ze ścieżką ./something.

Ta linia jądra zostaje wywołana w pliku przekazanym do exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Odczytuje pierwsze bajty pliku i porównuje je #!.

Jeśli porównanie jest prawdziwe, reszta linii jest analizowana przez jądro Linuksa, które wykonuje kolejne execwywołanie ze ścieżką /usr/bin/env pythoni bieżącym plikiem jako pierwszym argumentem:

/usr/bin/env python /path/to/script.py

i działa to na każdy język skryptowy, który używa #jako znaku komentarza.

I tak, możesz wykonać nieskończoną pętlę za pomocą:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash rozpoznaje błąd:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! akurat jest czytelny dla ludzi, ale nie jest to wymagane.

Jeśli plik zaczął się od różnych bajtów, execwywołanie systemowe użyłoby innej procedury obsługi. Innym najważniejszym wbudowanym modułem obsługi są pliki wykonywalne ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, który sprawdza bajty 7f 45 4c 46(które również są ludźmi) czytelne dla .ELF). Potwierdźmy to, czytając 4 pierwsze bajty /bin/ls, czyli plik wykonywalny ELF:

head -c 4 "$(which ls)" | hd 

wynik:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Więc kiedy jądro zobaczy te bajty, pobiera plik ELF, poprawnie zapisuje go w pamięci i rozpoczyna z nim nowy proces. Zobacz także: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Na koniec możesz dodać własne binfmt_miscmechanizmy shebang z mechanizmem. Na przykład możesz dodać niestandardowy moduł obsługi .jarplików . Ten mechanizm obsługuje nawet programy obsługi według rozszerzeń plików. Inną aplikacją jest przezroczyste uruchamianie plików wykonywalnych o innej architekturze za pomocą QEMU .

Nie sądzę jednak, że POSIX określa shebangs: https://unix.stackexchange.com/a/346214/32558 , chociaż wspomina o tym w sekcjach uzasadnienia oraz w formie „jeśli skrypty wykonywalne są obsługiwane przez system, coś może się zdarzyć".

Ciro Santilli
źródło
1
Uruchomienie ./somethingz powłoki nie przejdzie pełnej ścieżki do exec, ale dokładnie ścieżkę wprowadzoną. Czy możesz to poprawić w swojej odpowiedzi? Zrób echo "$0"w swoim skrypcie, a zobaczysz, że tak jest.
AndiDog
2

W rzeczywistości, jeśli weźmiesz to konsekwentnie, plik wykonywalny zanotowany w linii shebang jest tylko plikiem wykonywalnym. Sensowne jest użycie interpretera tekstu jako pliku wykonywalnego, ale nie jest to konieczne. Dla wyjaśnienia i demonstracji zrobiłem raczej bezużyteczny test:

#!/bin/cat
useless text
more useless text
still more useless text

Nazwie Test.txt pliku i ustawić wykonywalne bit chmod u+x test.txt, a następnie „o nazwie” go: ./test.txt. Zgodnie z oczekiwaniami zawartość pliku jest wyprowadzana. W tym przypadku kot nie ignoruje linii shebang. Po prostu wyświetla wszystkie linie. Każdy przydatny tłumacz powinien zatem móc zignorować tę linię shebang. W przypadku bash, perl i PHP jest to po prostu wiersz komentarza. Więc tak, te ignorują linię shebang.

Zygfryd
źródło
-1

Z tego, co zebrałem, za każdym razem, gdy plik ma ustawiony bit wykonywalny i jest wywoływany, jądro analizuje nagłówek pliku w celu ustalenia, jak kontynuować (o ile wiem, możesz dodać niestandardowe programy obsługi niestandardowych formatów plików za pomocą LKM). Jeśli plik wydaje się być plikiem tekstowym o numerze #! kombinacja na początku, jej wykonanie jest wysyłane do innego pliku wykonywalnego (zwykle rodzaju powłoki), którego ścieżkę należy określić bezpośrednio po tym shebang, w tym samym wierszu. Jądro następnie wykonuje powłokę i przekazuje plik do obsłużenia.

Krótko mówiąc, nie ma znaczenia, z której powłoki wywołujesz skrypt - jądro wyśle ​​wykonanie w odpowiedni sposób.

Vladimir Parka
źródło
4
Istnieje wyraźna różnica między bash ./myscript.shi ./myscript.sh.
CVn
Co rozumiesz przez tę „wyraźną różnicę”?
jrara,
3
@ jrara Zobacz moją odpowiedź, stwierdzenie, że „nie ma znaczenia, z której powłoki wywołujesz skrypt”, jest po prostu nieprawdą.
Thomas Nyman