В чем разница между "#!/Usr/bin/env bash" и "#!/Usr/bin/bash"?
В чем разница между этими двумя операторами в заголовке bash
скрипта?
-
#!/usr/bin/env bash
-
#!/usr/bin/bash
Когда я попытался увидеть env
страницу env
, я получил это определение:
env - run a program in a modified environment
Что это значит?
Ответы
Ответ 1
Выполнение команды через /usr/bin/env
имеет преимущество ищет то, что по умолчанию версия программы находится в текущем окр ironment.
Таким образом, вам не нужно искать его в определенном месте в системе, поскольку эти пути могут находиться в разных местах в разных системах. Пока он на вашем пути, он его найдет.
Недостатком является то, что вы не сможете передать более одного аргумента (например, вы не сможете написать /usr/bin/env awk -f
), если вы хотите поддерживать Linux, поскольку POSIX расплывчато в том, как это сделать. интерпретировать, и Linux интерпретирует все после первого пробела, чтобы обозначить один аргумент. Вы можете использовать /usr/bin/env -S
на некоторых версиях env
чтобы обойти это, но тогда сценарий станет еще менее переносимым и сломается на довольно недавних системах (например, даже на Ubuntu 16.04, если не позже).
Другим недостатком является то, что, поскольку вы не вызываете явный исполняемый файл, он может стать причиной ошибок и проблем с безопасностью многопользовательских систем (например, если кому-то удалось получить свой исполняемый файл с именем bash
на вашем пути).
#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash #gives you explicit control on a given system of what executable is called
В некоторых ситуациях первое может быть предпочтительным (например, запуск сценариев Python с несколькими версиями Python без необходимости переделки исполняемой строки). Но в ситуациях, когда безопасность находится в центре внимания, последнее предпочтительнее, так как оно ограничивает возможности внедрения кода.
Ответ 2
Использование #!/usr/bin/env NAME
заставляет оболочку искать первое соответствие NAME в переменной среды $PATH. Это может быть полезно, если вы не знаете об абсолютном пути или не хотите его искать.
Ответ 3
Вместо того, чтобы явно определять путь к интерпретатору, как в /usr/bin/bash/
, с помощью команды env поиск интерпретатора и запускается там, где он был впервые найден. Это имеет как плюсы, так и минусы
Ответ 4
Я считаю это полезным, потому что, когда я не знал об env, прежде чем начал писать script, я делал это:
type nodejs > scriptname.js #or any other environment
а затем я изменял эту строку в файле в shebang.
Я делал это, потому что я не всегда помнил, где nodejs на моем компьютере -/usr/bin/или/bin/, поэтому для меня env
очень полезно. Возможно, есть подробности с этим, но это моя причина.
Ответ 5
Если сценарии оболочки начинаются с #!/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 "[email protected]"
Давайте сделаем простой скрипт 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
.