Если сценарии оболочки начинаются с #!/bin/bash
, они всегда будут запускаться с bash
из /bin
. Однако если они начнут с #!/usr/bin/env bash
, они будут искать bash
в $PATH
, а затем начнут с первого, которое смогут найти.
Почему это может быть полезно? Предположим, вы хотите запускать bash
сценарии, для которых требуется bash 4.x или новее, но в вашей системе установлено только bash
3.x, и в настоящее время ваш дистрибутив не предлагает более новую версию, или вы не являетесь администратором и не можете изменить то, что установлено на эта система.
Конечно, вы можете загрузить исходный код bash и создать свой собственный bash с нуля, например, поместив его в ~/bin
. И вы также можете изменить свою $PATH
переменную в вашем .bash_profile
файле, чтобы включить ~/bin
в качестве первой записи (PATH=$HOME/bin:$PATH
, поскольку ~
не будет расширяться в $PATH
). Если вы сейчас вызовете bash
, оболочка сначала будет искать его в $PATH
по порядку, поэтому она начинается с ~/bin
, где и найдет ваш bash
. То же самое происходит, если сценарии ищут bash
с помощью #!/usr/bin/env bash
, поэтому теперь эти сценарии будут работать в вашей системе с использованием вашей пользовательской сборки bash
.
Одним из недостатков является то, что это может привести к неожиданному поведению, например один и тот же сценарий на одной машине может работать с разными интерпретаторами для разных сред или пользователей с разными путями поиска, вызывая всевозможные головные боли.
Самым большим недостатком env
является то, что некоторые системы допускают только один аргумент, поэтому вы не можете этого сделать #!/usr/bin/env <interpreter> <arg>
, поскольку системы будут рассматривать <interpreter> <arg>
как один аргумент (они будут рассматривать его, как если бы выражение было заключено в кавычки), и поэтому env
будет искать интерпретатор по имени <interpreter> <arg>
. Обратите внимание, что это проблема не самой команды env
, которая всегда позволяла передавать несколько параметров, а парсера shebang системы, который анализирует эту строку даже перед вызовом env
. Между тем, это было исправлено в большинстве систем, но если ваш сценарий хочет быть сверхпортативным, вы не можете полагаться, что это было исправлено в системе, которую вы будете запускать.
Это может даже иметь последствия для безопасности, например если sudo
не был настроен для очистки среды или $PATH
был исключен из очистки. Позвольте мне продемонстрировать это:
Обычно /bin
- это хорошо защищенное место, только root
может там что-либо изменить. Ваш домашний каталог не является, однако, любая программа, которую вы запускаете, может вносить в него изменения. Это означает, что вредоносный код может поместить поддельный bash
в какой-то скрытый каталог, изменить ваш .bash_profile
, чтобы включить этот каталог в ваш $PATH
, так что все сценарии, использующие #!/usr/bin/env bash
, в конечном итоге будут работать с этим поддельным bash
. Если sudo
сохраняет $PATH
, у вас большие проблемы.
Например. Считайте, что инструмент создает файл ~/.evil/bash
со следующим содержанием:
#!/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 "$@"
Сделаем простой скрипт sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Подтверждение концепции (в системе, где sudo
сохраняет $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
Обычно все классические оболочки должны быть расположены в /bin
, и если вы не хотите размещать их там по какой-либо причине, на самом деле не проблема разместить символическую ссылку в /bin
, указывающую на их реальное местоположение (или, возможно, /bin
сам является символической ссылкой ), поэтому я всегда буду использовать #!/bin/sh
и #!/bin/bash
. Слишком много всего могло бы сломаться, если бы они больше не работали. Это не значит, что POSIX потребует эту позицию (POSIX не стандартизирует имена путей и, следовательно, вообще не стандартизирует функцию shebang), но они настолько распространены, что даже если система не предложит /bin/sh
, она, вероятно, все равно понимать #!/bin/sh
и знать, что с этим делать, возможно, это только для совместимости с существующим кодом.
Но для более современных, нестандартных, необязательных интерпретаторов, таких как Perl, PHP, Python или Ruby, на самом деле нигде не указано, где они должны быть расположены. Они могут быть в /usr/bin
, но они также могут быть в /usr/local/bin
или в совершенно другой ветви иерархии (/opt/...
, /Applications/...
и т. Д.). Вот почему они часто используют синтаксис #!/usr/bin/env xxx
shebang.
30.04.2019
#!/usr/bin/env echo Hello
жалуется:/usr/bin/env: echo Hello: No such file or directory
. Очевидно, он рассматриваетecho Hello
как единственный аргумент для/usr/bin/env
. 30.05.2014env
для GNU Coreutils позволяет передавать аргументы команде. Env FreeBSD также , похоже, поддерживает его. Что ты используешь? :) 18.12.2014env
, безусловно, позволяет передавать аргументы команде. Проблема заключается в семантике строки#!
, которая зависит от ядра. Последние ядра Linux допускают такие вещи, как#!/usr/bin/env command args
, но более старые ядра Linux и другие системы - нет. 18.12.2014