Czy #! / Bin / sh jest odczytywany przez tłumacza?

66

W bashlub shchyba wszystko, co zaczyna się od, #jest komentarzem .

Ale w bashskryptach piszemy:

#!/bin/bash

A w skryptach Python jest:

#!/bin/python

Czy to oznacza, że #sam w sobie jest komentarzem, a #!nie jest?

Gaurav Sharma
źródło
1
A kiedy zaczniesz przeglądać profile Apparmor, zobaczysz #include. Tam też #nie jest to komentarz.
4
@ vasa1 Ale kluczowym punktem, który często nie jest doceniany w wierszach hashbanga na początku skryptów powłoki, jest to, że komentarzami .
Eliah Kagan

Odpowiedzi:

100

#!Linia jest używana przed prowadzony jest skrypt, a następnie ignorowane , gdy skrypt jest uruchamiany.

Pytasz, jaka jest różnica między linią shebang a zwykłym komentarzem.

Linia zaczynająca się od #!jest tak samo komentarzem jak każda inna linia zaczynająca się od #. Dzieje się tak, jeśli #!jest to pierwszy wiersz pliku lub gdziekolwiek indziej. #!/bin/sh ma efekt , ale nie jest odczytywany przez samego tłumacza .

#nie jest komentarzem we wszystkich językach programowania, ale, jak wiecie, jest komentarzem w powłokach w stylu Bourne'a, w tym shi bash(a także w większości takich powłok csh). To także komentarz w Pythonie . Jest to komentarz w wielu plikach konfiguracyjnych, które wcale nie są skryptami (jak /etc/fstab).

Załóżmy, że skrypt powłoki zaczyna się od #!/bin/sh. To jest komentarz, a tłumacz (powłoka) ignoruje wszystko w wierszu po #znaku.

Celem #!linii nie jest udzielanie informacji tłumaczowi. Celem tego #!wiersza jest poinformowanie systemu operacyjnego (lub innego procesu uruchamiającego interpreter), co ma być używane jako interpreter .

  • Jeśli skrypt zostanie wywołany jako plik wykonywalny, na przykład przez uruchomienie ./script.sh, system przejdzie do pierwszego wiersza, aby sprawdzić, czy zaczyna się od #!, po którym następuje zero lub więcej spacji, a następnie polecenie. Jeśli tak, uruchamia to polecenie z nazwą skryptu jako argumentu. W tym przykładzie działa /bin/sh script.sh(lub technicznie /bin/sh ./script.sh).

  • Jeśli skrypt zostanie wywołany przez jawne wywołanie interpretera, #!linia nigdy nie zostanie sprawdzona. Więc jeśli uruchomisz sh script.sh, pierwsza linia nie ma wpływu. Jeśli script2.shpierwszym wierszem jest #!/usr/games/nibbles, uruchomienie sh script2.shnie spróbuje otworzyć skryptu w nibbles(ale ./script2.shzrobi).

Zauważysz, że w żadnym przypadku rozszerzenie skryptu ( .sh), jeśli takie posiada, nie wpływa na sposób jego działania. W systemie uniksowym zwykle nie wpływa to na sposób uruchamiania skryptu. W niektórych innych systemach, takich jak Windows, #!linia shebang może być całkowicie ignorowana przez system, a rozszerzenie może określać, co uruchamia skrypty. (Nie oznacza to, że musisz podawać rozszerzenia skryptów, ale jest to jeden z powodów, dla których tak robisz, powinny być poprawne).

#!został wybrany, aby służyć temu celowi właśnie dlatego , że# zaczyna się komentarz. #!Linia jest w systemie, a nie tłumacz, i powinno być ignorowane przez interpreter.

Linia Shebang dla skryptów Bash

Ty (pierwotnie) powiedział użyć #!/bin/shdla bashskryptów. Powinieneś to zrobić tylko wtedy, gdy skrypt nie wymaga żadnego z bashrozszerzeń - shmusi być w stanie uruchomić skrypt. shnie zawsze jest dowiązaniem symbolicznym do bash. Często, w tym na wszystkich ostatnio zdalnych systemach Debian i Ubuntu , shjest dowiązaniem symbolicznym dash.

Linia Shebang dla skryptów Python

Powiedziałeś także (w pierwszej wersji swojego pytania, przed edycją), że uruchamiasz skrypty Pythona #!/bin/sh read by the interpretor. Jeśli masz na myśli dosłownie, zdecydowanie powinieneś przestać to robić. Jeśli hello.pyzaczyna się od tej linii, uruchamianie ./hello.pywykonuje:

/bin/sh read by the interpretor hello.py

/bin/shspróbuje wykonać skrypt o nazwie read(ze by the interpretor hello.pyswoimi argumentami), read(miejmy nadzieję) nie zostanie znaleziony, a twój skrypt w Pythonie nigdy nie będzie widoczny dla interpretera Pythona.

Jeśli popełniasz ten błąd, ale nie mam problemu, który opisuję, prawdopodobnie python hello.pywywołujesz skrypty Pythona, jawnie określając interpreter (np. ), Co powoduje zignorowanie pierwszej linii. Kiedy dystrybuujesz swoje skrypty innym osobom lub używasz ich przez długi czas później, może nie być jasne, że jest to konieczne do ich działania. Najlepiej je teraz naprawić. Lub przynajmniej całkowicie usuń pierwszy wiersz, aby w przypadku niepowodzenia uruchomienia ./komunikat o błędzie miał sens.

W przypadku skryptów w języku Python, jeśli wiesz, gdzie jest (lub będzie) interpreter języka Python, możesz napisać #!wiersz w ten sam sposób:

#!/usr/bin/python

Lub, jeśli jest to skrypt w języku Python 3, należy określić python3, ponieważ pythonprawie zawsze jest to język Python 2 :

#!/usr/bin/python3

Problem polega jednak na tym, że chociaż /bin/shpowinien istnieć zawsze i /bin/bashprawie zawsze istnieje w systemach bashz systemem operacyjnym, Python może istnieć w różnych miejscach.

Dlatego wielu programistów Python używa tego zamiast tego:

#!/usr/bin/env python

(Lub #!/usr/bin/env python3dla Python 3.)

To sprawia, że ​​skrypt polega na envbyciu w „właściwym miejscu” zamiast na pythonbyciu we właściwym miejscu. To dobrze, ponieważ:

  • envprawie zawsze znajduje się w /usr/bin.
  • W większości systemów, w zależności od tego, który skrypt python powinien zostać uruchomiony, jest ten, który pojawia się jako pierwszy w PATH. Począwszy hello.pyod #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, który jest (praktycznie) równoważny z bieganiem python hello.py.

Nie możesz użyć #!pythontego, że:

  • Chcesz, aby określony interpreter był podawany przez ścieżkę bezwzględną (tzn. Zaczynając od /).
  • Proces wywoływania byłby wykonywany python w bieżącym katalogu . Przeszukiwanie ścieżki, gdy polecenie nie zawiera ukośnika, jest specyficznym zachowaniem powłoki.

Czasami Python lub inny skrypt, który nie jest skryptem powłoki, będzie miał linię shebang zaczynającą się od miejsca, w #!/bin/sh ...którym ...znajduje się inny kod. Czasami jest to poprawne, ponieważ istnieją pewne sposoby wywołania powłoki ( sh) Bourne'a zgodnej z argumentami, aby wywołać interpreter Pythona. (Jeden z argumentów prawdopodobnie będzie zawierał python.) Jednak dla większości celów #!/usr/bin/env pythonjest prostszy, bardziej elegancki i bardziej prawdopodobne, że będzie działał tak, jak chcesz.

Linie Shebang w innych językach

Wiele języków programowania i skryptów oraz niektóre inne formaty plików służą #jako komentarz. Dla każdego z nich plik w języku może być uruchomiony przez program, który bierze go jako argument, określając program w pierwszym wierszu po #!.

W niektórych językach programowania #zwykle nie jest komentarzem, ale jako szczególny przypadek pierwszy wiersz jest ignorowany, jeśli zaczyna się od #!. Ułatwia to użycie #!składni, nawet jeśli w #innym przypadku nie czyni komentarza wierszem.

Linie Shebang dla plików, które nie działają jako skrypty

Chociaż jest to mniej intuicyjne, każdy plik, którego format pliku może pomieścić pierwszą linię rozpoczynającą się od #!następnej pełnej ścieżki pliku wykonywalnego, może mieć linię shebang. Jeśli to zrobisz, a plik zostanie oznaczony jako wykonywalny, możesz uruchomić go jak program ... powodując jego otwarcie jak dokument.

Niektóre aplikacje używają tego zachowania celowo. Na przykład w VMware .vmxpliki definiują maszyny wirtualne. Możesz „uruchomić” maszynę wirtualną tak, jakby to był skrypt, ponieważ pliki te są oznaczone jako pliki wykonywalne i mają linię shebang, która powoduje ich otwarcie w narzędziu VMware.

Linie Shebang dla plików, które nie działają jak skrypty, ale i tak działają jak skrypty

rmusuwa pliki. To nie jest język skryptowy. #!/bin/rmMożna jednak uruchomić plik, który zaczyna się i jest oznaczony jako wykonywalny, a po uruchomieniu rmwywoływany jest na nim, usuwając go.

Często jest to rozumiane jako „plik sam się usuwa”. Ale plik tak naprawdę wcale nie działa. Jest to bardziej podobne do sytuacji opisanej powyżej dla .vmxplików.

Ponieważ jednak #!wiersz ułatwia uruchomienie prostej komendy (w tym argumentów wiersza komend), możesz w ten sposób wykonać pewne skrypty. Jako prosty przykład „skryptu” bardziej zaawansowanego niż #!/bin/rm, rozważ:

#!/usr/bin/env tee -a

To pobiera interakcję użytkownika, odsyła go z powrotem do użytkownika wiersz po wierszu i dołącza na końcu pliku „skryptowego”.

Przydatny? Nie bardzo. Ciekawe koncepcyjnie? Całkowicie! Tak. (Nieco.)

Podobne koncepcyjnie koncepcje programowania / skryptowania (dla zabawy)

Eliah Kagan
źródło
@Rinzwind Thx! (Przy okazji ta odpowiedź nie pochodzi nigdzie indziej, jeśli się nad tym zastanawiasz).
Eliah Kagan
@Rinzwind Nie martw się, z 8 pozytywnymi opiniami po 1 godzinie prawdopodobnie
wzrośnie
1
Jeśli zawsze jest ignorowane, to co robi -xflaga Pythonów ?
gerrit
4
@gerrit Dobre pytanie. W każdym języku, w którym interpreter / kompilator zgłasza komunikaty z numerami wierszy, treść komentarzy jest ignorowana, ale wiersze komentarza są nadal liczone . Dodanie komentarza lub pustego wiersza przed wierszem kodu powoduje nadal zwiększenie tego wiersza kodu. -x„pomiń [s] pierwszą linię ...” druga linia jest numerowana 1zamiast 2, trzecia linia 2zamiast 3itd. Dlatego nie powinieneś używać tej flagi. ;) -xsłuży do tworzenia skryptów w systemach operacyjnych innych niż Unix, których składnia podobna do shebang nie zaczyna się od #(dlatego nie jest komentarzem w języku Python).
Eliah Kagan
4
W Perl, jeśli interpreter jest uruchamiany bezpośrednio ( perl script.plw porównaniu ./script.pl), a następnie interpreter będzie czytać wiersz shebang do analizowania takich jak flagi -w. Nie zaleca się jednak polegania na tej funkcji.
OrangeDog,
7

Shebang to sekwencja znaków składająca się ze znaku liczby znaków i wykrzyknika (np. „#!”), Gdy występuje jako początkowe dwa znaki w początkowej linii skryptu.

W systemach operacyjnych * nix, gdy uruchamiany jest skrypt rozpoczynający się od shebang, moduł ładujący program analizuje resztę początkowej linii skryptu jako dyrektywę interpretera; zamiast tego uruchamiany jest określony program interpretera, przekazując mu jako argument ścieżkę, która była początkowo używana podczas próby uruchomienia skryptu. Na przykład, jeśli skrypt ma nazwę ścieżki „path / to / your-script” i zaczyna się od następującego wiersza:

#!/bin/sh

następnie program ładujący program jest instruowany, aby zamiast tego uruchomić program „/ bin / sh”, np. powłokę Bourne'a lub kompatybilną powłokę, przekazując „path / to / your-script” jako pierwszy argument.

W związku z tym skrypt nosi nazwę ścieżki „path / to / python-script” i zaczyna się od następującego wiersza:

#!/bin/python

następnie załadowany program jest instruowany, aby zamiast tego uruchomić program „/ bin / python”, np. interpreter języka Python, przekazując „path / to / python-script” jako pierwszy argument.

W skrócie „#” skomentuje linię, a sekwencja znaków „#!” występowanie jako pierwszych dwóch znaków w początkowej linii skryptu ma znaczenie opisane powyżej.

Aby uzyskać szczegółowe informacje, zobacz Dlaczego niektóre skrypty zaczynają się od #! ...

Źródło: Niektóre sekcje tej odpowiedzi pochodzą (z niewielkimi modyfikacjami) z Shebang (Unix) na angielskiej Wikipedii (przez autorów Wikipedii ). Ten artykuł jest objęty licencją CC-BY-SA 3.0 , tak samo jak treść użytkownika tutaj w AU, więc to wyprowadzenie jest dozwolone z przypisaniem.

Goran Miskovic
źródło
4

#!nazywa się, shebanggdy występuje jako początkowe dwa znaki w początkowej linii skryptu. Jest używany w skryptach do wskazania interpretera do wykonania. shebangJest z systemu operacyjnego (kernel), a nie do osłony; więc nie będzie interpretowane jako komentarz.

Dzięki uprzejmości: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

Ogólnie, jeśli plik jest wykonywalny, ale tak naprawdę nie jest programem wykonywalnym (binarnym), a taka linia jest obecna, program określony po #! zaczyna się od skryptu i wszystkich jego argumentów. Te dwa znaki # i! muszą być pierwszymi dwoma bajtami w pliku!

Szczegółowe informacje: http://wiki.bash-hackers.org/scripting/basics#the_shebang

saji89
źródło
0

Nie, jest używany tylko przez execwywołanie systemowe jądra Linuksa i traktowany jako komentarz przez tłumacza

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 zdarzyć".

Ciro Santilli
źródło