В чем разница между этими двумя операторами в заголовке Bash-скрипта:
#!/usr/bin/env bash
#!/usr/bin/bash
Когда я сверился со env
справочной страницей , я получил это определение:
env - run a program in a modified environment
Что это значит?
В чем разница между этими двумя операторами в заголовке Bash-скрипта:
#!/usr/bin/env bash
#!/usr/bin/bash
Когда я сверился со env
справочной страницей , я получил это определение:
env - run a program in a modified environment
Что это значит?
Ответы:
Выполнение команды сквозной /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 без необходимости переделки исполняемой строки). Но в ситуациях, когда безопасность находится в центре внимания, последнее предпочтительнее, так как оно ограничивает возможности внедрения кода.
#!/usr/bin/env echo Hello
жалуется: /usr/bin/env: echo Hello: No such file or directory
. По-видимому, это рассматривается echo Hello
как один аргумент /usr/bin/env
.
env
команда, безусловно, позволяет передавать аргументы команде. Проблема заключается в семантике #!
строки, и это зависит от ядра. Последние ядра Linux допускают такие вещи, как #!/usr/bin/env command args
старые ядра Linux и другие системы.
Использование #!/usr/bin/env NAME
заставляет оболочку искать первое совпадение NAME в переменной окружения $ PATH. Это может быть полезно, если вы не знаете об абсолютном пути или не хотите его искать.
Вместо того, чтобы явно определять путь к интерпретатору, как /usr/bin/bash/
при использовании команды env, интерпретатор ищется и запускается оттуда, где он был впервые найден. Это имеет как плюсы, так и минусы
Если сценарии оболочки начинаются с #!/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
синтаксис Шебанга.
Я нахожу это полезным, потому что, когда я не знал об env, до того, как начал писать скрипт, я делал это:
type nodejs > scriptname.js #or any other environment
а потом я модифицировал эту строку в файле в shebang.
Я делал это, потому что я не всегда помнил, где находится nodejs на моем компьютере - / usr / bin / или / bin /, поэтому для меня env
это очень полезно. Может быть, есть детали с этим, но это моя причина