it-roy-ru.com

В чем разница между "#! / Usr / bin / env bash" и "#! / Usr / bin / bash"?

В чем разница между этими двумя операторами в заголовке скрипта bash?

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Когда я попытался просмотреть справочную страницу env, я получил следующее определение:

 env - run a program in a modified environment

Что это значит?

291
tarrsalah

Выполнение команды через /usr/bin/env имеет преимущество поиска любой версии программы по умолчанию в вашей текущей среде env .

Таким образом, вам не нужно искать его в определенном месте в системе, поскольку эти пути могут находиться в разных местах в разных системах. Пока он на вашем пути, он его найдет.

Недостатком является то, что вы не сможете передать более одного аргумента (например, вы не сможете написать /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 без необходимости переделки исполняемой строки). Но в ситуациях, когда безопасность находится в центре внимания, последнее предпочтительнее, так как оно ограничивает возможности внедрения кода.

264
Alec Bennett

Использование #!/usr/bin/env NAME заставляет Shell искать первое совпадение NAME в переменной среды $ PATH. Это может быть полезно, если вы не знаете об абсолютном пути или не хотите его искать.

49
Dolphiniac

Вместо явного определения пути к интерпретатору, как в /usr/bin/bash/, с помощью команды env ищется и запускается интерпретатор оттуда, где он был впервые найден. Это имеет как плюсы и минусы

11
safay

Я нахожу это полезным, потому что, когда я не знал об env, до того, как начал писать скрипт, я делал это:

type nodejs > scriptname.js #or any other environment

а потом я модифицировал эту строку в файле в Шебанг.
Я делал это, потому что я не всегда помнил, где находится nodejs на моем компьютере -/usr/bin/или/bin /, поэтому для меня env очень полезен. Может быть, есть детали с этим, но это моя причина

3
sudo97

Если сценарии оболочки начинаются с #!/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 Шебанга.

2
Mecki