Jeśli skrypty powłoki zaczynają się od #!/bin/bash
, zawsze będą działać bash
od /bin
. Jeśli jednak zaczną od #!/usr/bin/env bash
, będą wyszukiwać bash
w, $PATH
a następnie zaczną od pierwszego, jaki znajdą.
Dlaczego miałoby to być przydatne? Załóżmy, że chcesz uruchamiać bash
skrypty, które wymagają wersji bash 4.x lub nowszej, ale twój system ma tylko bash
zainstalowaną wersję 3.x, a obecnie twoja dystrybucja nie oferuje nowszej wersji lub nie jesteś administratorem i nie możesz zmienić tego, co jest zainstalowane w tym systemie .
Oczywiście możesz pobrać kod źródłowy bash i zbudować własny bash od zera, umieszczając go ~/bin
na przykład. Możesz także zmodyfikować $PATH
zmienną w swoim .bash_profile
pliku, aby uwzględnić ją ~/bin
jako pierwszy wpis ( PATH=$HOME/bin:$PATH
ponieważ ~
nie będzie się rozwijał $PATH
). Jeśli teraz zadzwonisz bash
, powłoka najpierw poszuka go $PATH
w kolejności, więc zaczyna się od miejsca ~/bin
, w którym znajdzie twój bash
. To samo dzieje się, jeśli skrypty szukają bash
użycia #!/usr/bin/env bash
, więc te skrypty będą teraz działać w systemie przy użyciu niestandardowej bash
kompilacji.
Jednym minusem jest to, że może to prowadzić do nieoczekiwanego zachowania, np. Ten sam skrypt na tej samej maszynie może działać z różnymi interpretatorami dla różnych środowisk lub użytkowników z różnymi ścieżkami wyszukiwania, powodując różnego rodzaju bóle głowy.
Największym minusem env
jest to, że niektóre systemy zezwalają tylko na jeden argument, więc nie można tego zrobić #!/usr/bin/env <interpreter> <arg>
, ponieważ systemy będą postrzegane <interpreter> <arg>
jako jeden argument (będą traktować to tak, jakby wyrażenie było cytowane), a zatem env
będą szukać tłumacza o nazwie <interpreter> <arg>
. Zauważ, że nie jest to problem env
samej komendy, która zawsze pozwalała na przesłanie wielu parametrów, ale z parserem shebang systemu, który analizuje tę linię przed nawet wywołaniem env
. Tymczasem zostało to naprawione w większości systemów, ale jeśli twój skrypt chce być bardzo przenośny, nie możesz polegać na tym, że zostało to naprawione w systemie, w którym będziesz działał.
Może nawet mieć wpływ na bezpieczeństwo, np. Jeśli sudo
nie został skonfigurowany do czyszczenia środowiska lub $PATH
został wykluczony z czyszczenia. Pokażę to:
Zazwyczaj /bin
jest to dobrze chronione miejsce, tylko root
tam może coś zmienić. Twój katalog domowy nie jest jednak w żadnym programie, który uruchamiasz, może wprowadzać w nim zmiany. Oznacza to, że złośliwy kod może umieścić fałszywy bash
w jakimś ukrytym katalogu, zmodyfikować go tak, .bash_profile
aby zawierał ten katalog w twoim $PATH
, aby wszystkie używane skrypty #!/usr/bin/env bash
działały z tym fałszywym bash
. Jeśli sudo
tak się stanie $PATH
, masz duże kłopoty.
Np. Rozważ narzędzie, które tworzy plik ~/.evil/bash
o następującej treści:
#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "$@"
Stwórzmy prosty skrypt sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Dowód koncepcji (w systemie, w którym sudo
zachowuje $PATH
):
$ ./sample.sh
Hello World
$ sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ sudo ./sample.sh
All your base are belong to us...
Hello World
Zwykle wszystkie powłoki powinny znajdować się w /bin
środku, a jeśli nie chcesz ich tam umieszczać z jakiegokolwiek powodu, naprawdę nie jest problemem umieszczanie dowiązania symbolicznego w /bin
tych punktach do ich prawdziwych lokalizacji (a może /bin
sam jest dowiązaniem symbolicznym), więc Zawsze chodziłbym z #!/bin/sh
i #!/bin/bash
. Jest zbyt wiele, co by się zepsuło, gdyby te nie działały. To nie jest tak, że POSIX wymagałby takiej pozycji (POSIX nie standaryzuje nazw ścieżek, a tym samym wcale nie standaryzuje funkcji shebang), ale są one tak powszechne, że nawet jeśli system nie oferowałby /bin/sh
, prawdopodobnie by to zrozumiał #!/bin/sh
i wiedzieć, co z tym zrobić i może to być tylko dla zgodności z istniejącym kodem.
Ale w przypadku bardziej nowoczesnych, niestandardowych, opcjonalnych interpreterów, takich jak Perl, PHP, Python lub Ruby, nie jest tak naprawdę nigdzie określone, gdzie powinny się znajdować. Mogą być /usr/bin
, ale mogą także znajdować się w /usr/local/bin
lub w zupełnie innej branży (hierarchii /opt/...
, /Applications/...
itp). Dlatego często używają #!/usr/bin/env xxx
składni shebang.