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

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

  1. #!/usr/bin/env bash

  2. #!/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.