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

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

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

Ответ 1

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

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

Ответ 2

Стандартное расположение 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 shebang. Таким образом, когда я запускаю свои сценарии оболочки, я получаю эту старую и паршивую версию bash, которая даже не имеет ассоциативных массивов.

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

Редко видеть это с помощью bash, но он намного чаще встречается с Perl и Python:

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

 #!/usr/bin/env bash

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

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

env

Он напечатает все переменные среды.

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

echo $bash.

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

Ответ 6

Я бы предпочел обернуть основную программу в скрипт, как показано ниже, чтобы проверить все 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 "[email protected]"

Ответ 7

Как мы можем быть уверены, что env существует и даже находится как /usr/bin/env?