Zauważam, że niektóre skrypty, które nabyłem od innych, mają shebang, #!/path/to/NAME
podczas gdy inne (używając tego samego narzędzia, NAME) mają shebang #!/usr/bin/env NAME
.
Oba wydają się działać poprawnie. W samouczkach (na przykład w języku Python) wydaje się, że istnieje sugestia, że ten ostatni shebang jest lepszy. Ale nie do końca rozumiem, dlaczego tak jest.
Zdaję sobie sprawę z tego, że aby użyć tego ostatniego szangletu, NAZWA musi znajdować się w ŚCIEŻCE, podczas gdy pierwszy szejk nie ma tego ograniczenia.
Wydaje mi się także (dla mnie), że pierwszy byłby lepszym shebangiem, ponieważ precyzuje dokładnie, gdzie znajduje się NAME. W takim przypadku, jeśli istnieje wiele wersji NAME (np. / Usr / bin / NAME, / usr / local / bin / NAME), pierwszy przypadek określa, którego użyć.
Moje pytanie brzmi: dlaczego pierwszy shebang jest lepszy od drugiego?
źródło
Odpowiedzi:
To niekoniecznie lepsze.
Zaletą
#!/usr/bin/env python
jest to, że użyje dowolnegopython
pliku wykonywalnego, który pojawi się jako pierwszy w użytkowniku$PATH
.Wadą z
#!/usr/bin/env python
jest to, że będzie używać cokolwiekpython
wykonywalny pierwszy pojawia się w instrukcji$PATH
.Oznacza to, że skrypt może zachowywać się inaczej w zależności od tego, kto go uruchamia. W przypadku jednego użytkownika może korzystać z
/usr/bin/python
zainstalowanego z systemem operacyjnym. Po drugie, może użyć eksperymentu/home/phred/bin/python
, który nie działa poprawnie.A jeśli
python
jest zainstalowany tylko/usr/local/bin
użytkownik, który nie ma/usr/local/bin
na$PATH
nie nawet w stanie uruchomić skrypt. (Prawdopodobnie nie jest to zbyt prawdopodobne w nowoczesnych systemach, ale może się to zdarzyć w przypadku bardziej niejasnego tłumacza.)Określając
#!/usr/bin/python
, dokładnie określasz, który interpreter będzie używany do uruchomienia skryptu w określonym systemie .Innym potencjalnym problemem jest to, że
#!/usr/bin/env
sztuczka nie pozwala ci przekazywać argumentów do interentera (innych niż nazwa skryptu, która jest przekazywana niejawnie). To zwykle nie jest problemem, ale może być. Wiele skryptów Perla jest napisanych przy użyciu#!/usr/bin/perl -w
, ale obecnieuse warnings;
jest to zalecany zamiennik. Skrypty#!/bin/csh -f
csh powinny być używane - ale skrypty csh nie są zalecane . Ale mogą być inne przykłady.Mam wiele skryptów Perla w osobistym systemie kontroli źródła, które instaluję, kiedy zakładam konto w nowym systemie. Używam skryptu instalatora, który modyfikuje
#!
wiersz każdego skryptu, gdy instaluje go w moim$HOME/bin
. (Nie musiałem używać niczego innego niż#!/usr/bin/perl
ostatnio; to sięga czasów, gdy Perl często nie był domyślnie instalowany).Drobna uwaga:
#!/usr/bin/env
sztuczka jest prawdopodobnie nadużyciemenv
polecenia, które pierwotnie miało (jak sama nazwa wskazuje) wywoływać polecenie w zmienionym środowisku. Ponadto niektóre starsze systemy (w tym SunOS 4, jeśli dobrze pamiętam) nie miały tegoenv
polecenia/usr/bin
. Żaden z nich prawdopodobnie nie będzie stanowić poważnego problemu.env
działa w ten sposób, wiele skryptów używa tej#!/usr/bin/env
sztuczki, a dostawcy systemów operacyjnych prawdopodobnie nie zrobią nic, aby ją złamać. Może to być problem, jeśli chcesz, aby skrypt działał na naprawdę starym systemie, ale prawdopodobnie i tak będziesz musiał go zmodyfikować.Innym możliwym problemem (dzięki Sopalajo de Arrierez za wskazanie tego w komentarzach) jest to, że zadania cron działają w ograniczonym środowisku. W szczególności
$PATH
jest to zwykle coś w rodzaju/usr/bin:/bin
. Więc jeśli katalog zawierający interpreter nie znajduje się w jednym z tych katalogów, nawet jeśli jest domyślnie$PATH
w powłoce użytkownika,/usr/bin/env
sztuczka nie zadziała. Możesz podać dokładną ścieżkę lub dodać linię do swojego crontab, aby ustawić$PATH
(man 5 crontab
szczegółowe informacje).źródło
use v5.12;
służy niektóre z tych celów. I#!/usr/bin/env perl5.12
zawiedzie, jeśli system ma Perla 5.14, ale nie 5.12. Dla Python 2 vs. 3#!/usr/bin/python2
i#!/usr/bin/python3
prawdopodobnie będą działać./usr/local/bin
. Zdarza mi się wiedzieć, że działa poprawnie/usr/bin/perl
. Nie mam pojęcia, czy działa z dowolnymperl
plikiem wykonywalnym, który przypadkowo ma u niego jakiś przypadkowy użytkownik$PATH
. Może ktoś eksperymentuje z jakąś starożytną wersją Perla; ponieważ określiłem#!/usr/bin/perl
, mój skrypt (którego użytkownik niekoniecznie zna lub obchodzi się ze skryptem Perla) nie przestanie działać./usr/bin/perl
, dowiem się bardzo szybko, a obowiązkiem właściciela / administratora systemu jest aktualizowanie go. Jeśli chcesz uruchomić mój skrypt z własnym perlem, możesz pobrać i zmodyfikować kopię lub wywołać ją za pośrednictwemperl foo
. (I możesz wziąć pod uwagę możliwość, że 55 osób, które głosowały za odpowiedzią, również wiedzą coś lub dwa. Z pewnością możliwe jest, że masz rację i oni wszyscy się mylą, ale nie tak się zakładam).Ponieważ / usr / bin / env może interpretować twoje
$PATH
, co czyni skrypty bardziej przenośnymi.Uruchomi skrypt tylko, jeśli Python jest zainstalowany w / usr / local / bin.
Zinterpretuje twój
$PATH
i znajdzie python w dowolnym katalogu w twoim$PATH
.Więc twój skrypt jest bardziej mobilny i będzie działać bez żadnych modyfikacji w systemach, w których zainstalowany jest jak pyton
/usr/bin/python
, lub/usr/local/bin/python
, lub nawet niestandardowych katalogów (które zostały dodane do$PATH
), jak/opt/local/bin/python
.Przenośność jest jedynym powodem, dla którego użycie
env
jest preferowane zamiast ścieżek zakodowanych na stałe.źródło
python
plików wykonywalnych są szczególnie powszechne wraz zevirtualenv
wzrostem użycia.#! python
dlaczego nie jest to używane?#! python
nie jest używany, ponieważ musiałbyś znajdować się w tym samym katalogu co plik binarny Pythona, ponieważ słowopython
to jest interpretowane jako pełna ścieżka do pliku. Jeśli nie masz pliku binarnego Python w bieżącym katalogu, pojawi się błąd typubash: ./script.py: python: bad interpreter: No such file or directory
. To tak, jakbyś używał#! /not/a/real/path/python
Określenie ścieżki bezwzględnej jest bardziej precyzyjne w danym systemie. Minusem jest to, że jest zbyt precyzyjne. Załóżmy, że zdajesz sobie sprawę, że systemowa instalacja Perla jest zbyt stara dla twoich skryptów i zamiast tego chcesz użyć własnej: wtedy musisz edytować skrypty i zmienić
#!/usr/bin/perl
na#!/home/myname/bin/perl
. Co gorsza, jeśli masz Perla/usr/bin
na niektórych komputerach,/usr/local/bin
na innych i/home/myname/bin/perl
jeszcze na innych komputerach, będziesz musiał zachować trzy osobne kopie skryptów i wykonać odpowiedni na każdym komputerze.#!/usr/bin/env
psuje się, jeśliPATH
jest źle, ale robi prawie wszystko. Próba działania ze złymPATH
jest bardzo rzadko przydatna i wskazuje, że wiesz bardzo mało o systemie, w którym działa skrypt, więc nie możesz polegać na żadnej bezwzględnej ścieżce.Istnieją dwa programy, w których lokalizacji można polegać na prawie każdym wariancie uniksowym:
/bin/sh
i/usr/bin/env
. Niektóre niejasne i przeważnie wycofane warianty uniksowe miały je/bin/env
bez/usr/bin/env
, ale raczej ich nie spotkasz. Nowoczesne systemy mają/usr/bin/env
właśnie swoje szerokie zastosowanie w shebangach./usr/bin/env
jest coś, na co możesz liczyć.Poza
/bin/sh
tym jedyną sytuacją, w której powinieneś użyć bezwzględnej ścieżki w shebang, jest to, że twój skrypt nie ma być przenośny, więc możesz liczyć na znaną lokalizację dla tłumacza. Na przykład można bezpiecznie używać skryptu bash, który działa tylko w systemie Linux#!/bin/bash
. Skrypt przeznaczony wyłącznie do użytku wewnętrznego może polegać na konwencjach lokalizacji interpretera domowego.#!/usr/bin/env
ma wady. Jest bardziej elastyczny niż określenie ścieżki bezwzględnej, ale nadal wymaga znajomości nazwy tłumacza. Czasami możesz chcieć uruchomić interpreter, którego nie ma$PATH
, na przykład w lokalizacji względem skryptu. W takich przypadkach często można utworzyć skrypt poliglota, który może być interpretowany zarówno przez standardową powłokę, jak i przez żądanego tłumacza. Na przykład, aby uczynić skrypt Python 2 przenośnym zarówno dla systemów, w którychpython
jest Python 3 ipython2
Python 2, jak i dla systemów, w którychpython
Python 2 ipython2
nie istnieje:źródło
Używanie w Perlu
#!/usr/bin/env
jest złym pomysłem z dwóch powodów.Po pierwsze, nie jest przenośny. Na niektórych niejasnych platformach env nie znajduje się w / usr / bin. Po drugie, jak zauważył Keith Thompson , może powodować problemy z przekazywaniem argumentów w linii shebang. Maksymalnie przenośne rozwiązanie to:
Aby uzyskać szczegółowe informacje na temat tego, jak to działa, zobacz „perldoc perlrun” i co mówi o argumencie -x.
źródło
Różnica między nimi wynika z tego, w jaki sposób skrypty są wykonywane.
Użycie
/usr/bin/env
(które, jak wspomniano w innych odpowiedziach, nie jest dostępne w/usr/bin
niektórych systemach operacyjnych) jest wymagane, ponieważ nie można po prostu umieścić nazwy pliku wykonywalnego po#!
- musi to być ścieżka bezwzględna. Wynika to z faktu, że#!
mechanizm działa na niższym poziomie niż powłoka. Jest częścią binarnego modułu ładującego jądra. Można to przetestować. Umieść to w pliku i zaznacz jako plik wykonywalny:Przekonasz się, że drukuje taki błąd podczas próby uruchomienia:
Jeśli plik jest oznaczony jako wykonywalny i zaczyna się od a
#!
, jądro (które nie wie o tym$PATH
lub o bieżącym katalogu: są to koncepcje użytkownika-użytkownika) szuka pliku przy użyciu bezwzględnej ścieżki. Ponieważ używanie ścieżki bezwzględnej jest problematyczne (jak wspomniano w innych odpowiedziach), ktoś wpadł na podstęp: możesz uruchomić/usr/bin/env
(który prawie zawsze jest w tej lokalizacji), aby uruchomić coś przy użyciu$PATH
.źródło
Istnieją jeszcze dwa problemy z używaniem
#!/usr/bin/env
Nie rozwiązuje problemu określenia pełnej ścieżki do tłumacza, po prostu ją przenosi
env
.env
więcej nie gwarantuje się w/usr/bin/env
niżbash
jest gwarancją w/bin/bash
lub w pyton/usr/bin/python
.env
zastępuje ARGV [0] nazwą interpretera (np. bash lub python).Zapobiega to pojawianiu się nazwy skryptu np. W
ps
danych wyjściowych (lub zmienia sposób / gdzie pojawia się) i uniemożliwia znalezienie go za pomocą np.ps -C scriptname.sh
[aktualizacja 2016-06-04]
I trzeci problem:
Zmiana ŚCIEŻKI to więcej pracy niż tylko edytowanie pierwszego wiersza skryptu, szczególnie gdy skryptowanie takich edycji jest banalne. na przykład:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Dołączanie lub oczekiwanie na katalog do $ PATH jest dość łatwe (chociaż nadal musisz edytować plik, aby stał się trwały - twój
~/.profile
lub cokolwiek innego - i daleko mu do łatwego skryptu TEGO edytowania, ponieważ PATH można ustawić w dowolnym miejscu skryptu , nie w pierwszym wierszu).Zmiana kolejności katalogów PATH jest znacznie trudniejsza ... i znacznie trudniejsza niż tylko edycja
#!
wiersza.I nadal masz wszystkie inne problemy, które
#!/usr/bin/env
daje ci używanie .@jlliagre sugeruje w komentarzu
#!/usr/bin/env
przydatnym do testowania skryptu z wieloma wersjami interpreterów, „zmieniając tylko ich kolejność ŚCIEŻKA / ŚCIEŻKA”Jeśli musisz to zrobić, o wiele łatwiej jest po prostu mieć kilka
#!
wierszy u góry skryptu (są to tylko komentarze w dowolnym miejscu, ale nie pierwszy wiersz) i wyciąć / skopiować i wkleić ten, którego chcesz teraz użyć, aby pierwsza linia.W
vi
to byłoby tak proste, jak przesunięcie kursora do żądanej#!
linii, a następnie wpisaniedd1GP
lubY1GP
. Nawet przy tak trywialnym edytorzenano
użycie myszki do skopiowania i wklejenia zajęłoby kilka sekund.Ogólnie rzecz biorąc, zalety korzystania
#!/usr/bin/env
są w najlepszym razie minimalne, a na pewno nie są nawet większe niż wady. Nawet przewaga „wygody” jest w dużej mierze iluzoryczna.IMO, to głupi pomysł promowany przez pewnego rodzaju programistę, który uważa, że systemy operacyjne nie są czymś, z czym można pracować, są problemem, który należy obejść (lub w najlepszym razie zignorować).
PS: oto prosty skrypt do zmiany interpretera wielu plików jednocześnie.
change-shebang.sh
:Uruchom jako np.
change-shebang.sh python2.7 *.py
Lubchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
źródło
csh
pisania skryptów promuje złe rozwiązanie: to działa, ale istnieją znacznie lepsze alternatywy.#!
wiersz./usr/bin/env
i muszę się go pozbyć lokalnie, lub jeśli nie korzysta z niego i muszę go dodać. Oba przypadki mogą wystąpić, dlatego skrypty przedstawione w tej odpowiedzi są potencjalnie przydatnym narzędziem w zestawie narzędzi.Dodając kolejny przykład tutaj:
Korzystanie
env
jest również przydatne, gdy na przykład chcesz udostępniać skrypty między wielomarvm
środowiskami.Uruchomienie tego w wierszu cmd pokazuje, która wersja Ruby będzie używana, gdy
#!/usr/bin/env ruby
zostanie użyta w skrypcie:env ruby --version
Dlatego, kiedy używasz
env
, możesz używać różnych wersji Ruby przez rvm, bez zmiany skryptów.źródło
Jeśli piszesz wyłącznie dla siebie lub dla swojej pracy, a lokalizacja tłumacza jest zawsze w tym samym miejscu, skorzystaj z bezpośredniej ścieżki. We wszystkich innych przypadkach użyj
#!/usr/bin/env
.Oto dlaczego: w twojej sytuacji
python
tłumacz był w tym samym miejscu, niezależnie od użytej składni, ale dla wielu osób mógł być zainstalowany w innym miejscu. Chociaż większość głównych tłumaczy programistycznych znajduje się w/usr/bin/
wielu nowszych programach domyślnie/usr/local/bin/
.Chciałbym nawet argumentować, aby zawsze używać,
#!/usr/bin/env
ponieważ jeśli masz wiele wersji tego samego interpretera i nie wiesz, jakie ustawienia domyślne ma twoja powłoka, prawdopodobnie powinieneś to naprawić.źródło
Ze względu na przenośność i kompatybilność lepiej jest używać
zamiast
Istnieje wiele możliwości, gdzie plik binarny może znajdować się w systemie Linux / Unix. Sprawdź stronę podręcznika hier (7), aby uzyskać szczegółowy opis hierarchii systemu plików.
Na przykład FreeBSD instaluje całe oprogramowanie, które nie jest częścią systemu podstawowego, w / usr / local / . Ponieważ bash nie jest częścią systemu podstawowego, plik bash jest instalowany w / usr / local / bin / bash .
Jeśli chcesz mieć przenośny skrypt bash / csh / perl / cokolwiek, który działa pod większością dystrybucji Linuksa i FreeBSD, powinieneś użyć #! / Usr / bin / env .
Zauważ też, że większość instalacji Linuksa również (twardo) połączyła plik binarny env z / bin / env lub linkami / usr / bin do / bin, które nie powinny być używane w shebang. Więc nie używaj #! / Bin / env .
źródło