Почему #! / Usr / bin / env bash превосходит #! / Bin / bash?

212

Я видел в нескольких местах, в том числе рекомендации на этом сайте ( что является предпочтительным Bash Shebang? ), Чтобы использовать #!/usr/bin/env bashв предпочтении #!/bin/bash. Я даже видел, как один предприимчивый человек предположил, что использование #!/bin/bashбыло неправильным, и функциональность bash была бы потеряна при этом.

Все это говорит о том, что я использую bash в жестко контролируемой тестовой среде, где каждый циркулирующий диск по сути является клоном одного главного диска. Я понимаю аргумент переносимости, хотя он не обязательно применим в моем случае. Есть ли какая-либо другая причина отдавать предпочтение #!/usr/bin/env bashальтернативам, и, принимая во внимание переносимость, есть ли какая-либо причина, по которой она может нарушить функциональность?

spugm1r3
источник
7
Это не обязательно лучше. Смотрите этот вопрос и мой ответ на unix.stackexchange.com. (Я бы проголосовал, чтобы закрыть это как дубликат, но я не думаю, что вы можете сделать это на разных сайтах.)
Кит Томпсон
2
В дополнение к ответу @ zigg envможет не находиться по адресу /usr/bin. Комментарии Шебанга вообще плохая идея ИМХО. Если ваш интерпретатор сценариев по умолчанию не обрабатывает комментарии shebang, это просто комментарий. Однако, если вы знаете, что интерпретатор сценариев может обрабатывать комментарии shebang, и вы знаете путь к bash, нет причин не вызывать его, используя его абсолютный путь, если путь не слишком длинный (маловероятный) или вы, возможно, портируете скрипт в систему, в которой bash не находится в / bin. С другой стороны, оговорки, о которых я упоминал ранее, применимы в этом случае, поскольку они связаны с переносимостью.
1
@KeithThompson, спасибо за ссылку. Возможно, мой поиск ответа до публикации вопроса был немного узким. Мой вывод от всего этого: (1) linux / unix / posix / etc ... серый, и (2) любой, кто утверждает, что имеет абсолютно правильный ответ, абсолютно правильно ответит для своего конкретного сценария.
spugm1r3
3
Поведение многих вещей в POSIX / Unix хорошо определено. Места не всегда такие четкие. Нечто должно существовать как /etcили /bin/sh. bashявляется надстройкой для большинства Unix-подобных систем. Это только Linux, где bashон гарантированно включен /binи, скорее всего, также связан как /bin/sh. Поскольку Linux для многих стал де-факто современным Unix, тот факт, что системы, отличные от Linux, могут существовать, был забыт. В моем собственном ответе ниже я принял Linux, потому что вы сказали bash. У многих коробок BSD, с которыми я работал, даже не было установлено.
Шон Перри
2
@Keith - В случае Bash (в отличие от Python в других вопросах) ... OpenBSD не имеет /bin/bash. Bash не устанавливается по умолчанию. Если вы хотите этого, вы должны pkg install bash. После установки он находится по адресу /usr/local/bin/bash. На /bin/bashOpenBSD ничего не установлено . Шебанг #!/bin/bashволи будет ошибкой и #!/usr/bin/env bashбудет успешным.
jww

Ответы:

229

#!/usr/bin/envпоиск PATHпо bash, и bashне всегда /bin, особенно в системах , отличных от Linux. Например, в моей системе OpenBSD она включена /usr/local/bin, поскольку она была установлена ​​как дополнительный пакет.

Если вы абсолютно уверены, что оно bashесть /binи будет всегда, нет ничего плохого в том, чтобы поместить это прямо в ваш шебанг, но я бы рекомендовал против этого, потому что у всех сценариев и программ есть жизни, превышающие то, что мы изначально считаем, что они будут иметь.

zigg
источник
29
как насчет envместоположения? POSIX не форсирует это.
Хулио Герра
1
@JulioGuerra Как и наличие /usr/lib/sendmail(или, совсем недавно /usr/sbin/sendmail) двоичного файла , доступного для обработки почты, в интересах Unix-подобной системы лучше всего это иметь, /usr/bin/envпотому что env shebang - такая распространенная практика. Это де-факто стандартный интерфейс.
зигг
8
@zigg Это так, UN * X ... <-: Я имею в виду, что в их интересах иметь стандартное местоположение для env, но каким-то образом не иметь стандартное местоположение (которое может быть просто мягкой ссылкой) bash. Не говоря уже о том, почему тогда hashbang не принимает просто #!bash, а использует PATH, вместо того, чтобы делать то же самое с env. Полагаю, не достаточно запутанным для новичков.
ддеканы
1
@JulioGuerra Согласно википедии вы правы: это не «гарантировано». Вместо этого это кажется «более вероятным». Википедия говорит This mostly works because the path /usr/bin/env is commonly used for the env utilityздесь en.wikipedia.org/wiki/Shebang_(Unix) - Мы должны доверять тому, envчтобы быть «вероятно» во всех системах.
Хави Монтеро
2
@XaviMontero: я использовал систему, в которой envбыла /bin, а не /usr/bin(не знаю какая, скорее всего SunOS 4). В эти дни это очень вероятно, /usr/bin/envбудет доступно, только из-за популярности #!/usr/bin/envвзлома.
Кит Томпсон
39

Стандартное расположение bash есть /bin, и я подозреваю, что это верно для всех систем. Однако что если вам не нравится эта версия bash? Например, я хочу использовать bash 4.2, но bash на моем Mac установлен на 3.2.5.

Я мог бы попытаться переустановить Bash, /binно это может быть плохой идеей. Если я обновлю свою ОС, она будет перезаписана.

Тем не менее, я мог бы установить bash /usr/local/bin/bashи настроить мой PATH на:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

Теперь, если я bashукажу, я получаю не старый грубый /bin/bash, а новый, более блестящий /usr/local/bin. Ницца!

За исключением того, что мои сценарии оболочки имеют этот !# /bin/bashшебанг. Таким образом, когда я запускаю свои сценарии оболочки, я получаю ту старую и паршивую версию bash, которая даже не имеет ассоциативных массивов.

Использование /usr/bin/env bashбудет использовать версию bash, найденную в моем PATH. Если я настрою свой PATH, чтобы /usr/local/bin/bashон выполнялся, это будет bash, который будут использовать мои скрипты.

Это редко можно увидеть в bash, но гораздо чаще встречается в Perl и Python:

  • Некоторые выпуски Unix / Linux, нацеленные на стабильность , иногда сильно отстают от выпуска этих двух языков сценариев. Не так давно Perl RHEL был на 5.8.8 - восьмилетняя версия Perl! Если кто-то хотел использовать более современные функции, вам нужно было установить собственную версию.
  • Такие программы, как Perlbrew и Pythonbrew, позволяют устанавливать несколько версий этих языков. Они зависят от сценариев, которые управляют вашим PATH, чтобы получить нужную версию. Жесткое кодирование пути означает, что я не могу запустить свой скрипт под brew .
  • Не так давно (хорошо, давно) Perl и Python не были стандартными пакетами, включенными в большинство систем Unix. Это означало, что вы не знали, где были установлены эти две программы. Это было под /bin? /usr/bin? /opt/bin? Кто знает? Использование #! /usr/bin/env perlозначало, что я не должен был знать.

И теперь, почему вы не должны использовать #! /usr/bin/env bash

Когда путь жестко запрограммирован в шебанге, я должен бежать с этим переводчиком. Таким образом, #! /bin/bashвынуждает меня использовать установленную по умолчанию версию bash. Поскольку возможности bash очень стабильны (попробуйте запустить версию Python 2.x под Python 3.x), очень маловероятно, что мой конкретный сценарий BASH не будет работать, и поскольку мой сценарий bash, вероятно, используется этой системой и другими системами Использование нестандартной версии bash может привести к нежелательным последствиям. Очень вероятно, что я хочу убедиться, что стабильная стандартная версия bash используется с моим сценарием оболочки. Таким образом, я, вероятно, хочу жестко прописать путь в моем шебанге.

Дэвид В.
источник
5
Это не так: «Стандартное расположение bash - это / bin» (если вы не можете сослаться на документ по стандартам), возможно, более точным является то, что это «обычное» расположение в большинстве дистрибутивов и макросов Linux, но это не стандарт для систем Unix в целом (и, в частности, не является местоположением на большинстве * bsds).
tesch1
13

Для вызова bashэто немного излишним. Если у вас нет нескольких bashдвоичных файлов, подобных вашему, в ~ / bin, но это также означает, что ваш код зависит от $ PATH, в котором есть нужные вещи.

Это удобно для таких вещей, как, pythonхотя. Существуют сценарии-оболочки и среды, которые приводят к использованию альтернативных pythonдвоичных файлов.

Но ничто не потеряно при использовании точного пути к двоичному файлу, если вы уверены, что это именно тот двоичный файл, который вам действительно нужен.

Шон Перри
источник
Пятно на для python. Я потерял счет количества мест, pythonкоторые можно найти 😀
зигг
2
Согласен, что /usr/bin/envэто более полезно для Python, особенно если вы используете virtualenv.
Деннис
10

Есть много систем, в которых нет Bash /bin, FreeBSD и OpenBSD, и это лишь некоторые из них. Если ваш сценарий предназначен для переноса во многие разные Unices, вы можете использовать #!/usr/bin/env bashвместо #!/bin/bash.

Обратите внимание, что это не относится к sh; для Bourne-совместимых скриптов я использовать исключительно #!/bin/sh, так как я думаю , что почти каждый Unix существует имеет shв /bin.

Джеймс Ко
источник
В Ubuntu 18.04 /bindir, я вижу sh -> dash. Символически связано с dashраскрытием природы Debian в Ubuntu. Запуск этих трех командных строк , чтобы понять , что все сводится к личным предпочтениям: which bashто which shпотом which dash.
Нообниня
0

Я бы предпочел обернуть основную программу в скрипт, как показано ниже, чтобы проверить все bashдоступные в системе. Лучше иметь больше контроля над версией, которую он использует.

#! /usr/bin/env bash

# This script just chooses the appropriate bash
# installed in system and executes testcode.main

readonly DESIRED_VERSION="5"

declare all_bash_installed_on_this_system
declare bash

if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
    found=0

    all_bash_installed_on_this_system="$(\
        awk -F'/' '$NF == "bash"{print}' "/etc/shells"\
        )"

    for bash in $all_bash_installed_on_this_system
    do
        versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
        [ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
    done
    if [ "${found}" -ne 1 ]
    then
        echo "${DESIRED_VERSION} not available"
        exit 1
    fi
fi

$bash main_program "$@"
Mihir
источник
0
 #!/usr/bin/env bash

определенно лучше, потому что он находит путь к исполняемому файлу bash из переменной системной среды.

Перейдите в свою оболочку Linux и введите

env

Он напечатает все ваши переменные окружения.

Перейдите к вашему сценарию оболочки и введите

echo $BASH

Он напечатает ваш путь bash (в соответствии со списком переменных среды), который вы должны использовать для построения вашего правильного пути shebang в вашем скрипте.

КТА
источник