Вопрос или проблема
На macOS #!/bin/bash
означает, что скрипт будет использовать системный bash, который очень устарел (3.2, 2006 год) и (по крайней мере, для меня) в большинстве случаев бесполезен.
Затем вы можете установить современный bash, используя Homebrew или MacPorts, и использовать что-то вроде этого:
#!/usr/bin/env bash
Тогда третий подход
#!/opt/homebrew/bin/bash
Но что, если вы запустите этот скрипт на Mac, где современный bash установлен с помощью MacPorts вместо Homebrew?
Какова наилучшая практика в этом случае? (Может быть, создать символическую ссылку в /usr/local/bin
, указывающую на /opt/homebrew/bin/bash
, а затем использовать #!/usr/local/bin
?)
#!/usr/bin/env bash
— лучшая практика, env — лучшая практика для всех shebang. MacOS или нет, нет гарантии, что ваша целевая система будет иметь ту же версию bash, что и система, на которой вы пишете скрипт.
Если вы хотите добиться истинной портативности, вам нужно использовать #!/bin/sh
, и даже тогда вам нужно знать, какие истинные POSIX-команды и методы доступны, потому что во многих системах /bin/sh просто является символической ссылкой на bash в режиме --posix
и все равно позволит использовать не POSIX вещи, такие как массивы. Также у вас нет контроля над любыми внешними командами awk
, sed
, grep
и другими, которые используются и будут различаться от системы к системе.
На macOS после свежей установки homebrew переменная $PATH выглядит так
% echo $PATH
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:...
% which -a bash
/opt/homebrew/bin/bash
/bin/bash
Так как homebrew размещает свою bin директорию перед /bin в $PATH
, и shell просматривает эту переменную с начала до конца при поиске исполняемых файлов, он будет запускать bash 5 без проблем с #!/usr/bin/env bash
.
Ответ или решение
На macOS использование shebang-системы и особенно выбора между различными версиями Bash — это зачастую нелегкая задача для разработчиков и системных администраторов. Исторически сложилось, что в macOS предустановлена устаревшая версия Bash 3.2, датированная 2006 годом. Это связано с лицензионными ограничениями GPL, из-за которых Apple прекратила обновлять Bash на своих системах. Однако современные требования и сценарии использования требуют более новых функций, доступных в последних версиях Bash.
Теория
Shebang — это строка, начинающаяся с #!
, расположенная в начале скрипта, которая указывает системе, какой интерпретатор использовать для выполнения данного скрипта. В контексте macOS, три основных подхода к использованию shebang включают:
-
Прямое указание на системный Bash:
#!/bin/bash
. Это приведет к использованию Bash 3.2, что не всегда приемлемо. -
Использование env для динамического поиска Bash:
#!/usr/bin/env bash
. Это подход, который позволяет системе найти bash в соответствии с переменной окружения PATH. Обычно он применяется для увеличения кроссплатформенности скриптов. -
Указание полного пути к современному Bash:
#!/opt/homebrew/bin/bash
или путь от MacPorts, в зависимости от того, как установлен современный Bash. Этот подход может вызывать проблемы, если скрипт выполняется на системе, где Bash установлен по другому пути.
Пример
Рассмотрите следующий сценарий: вы разрабатываете скрипт на macOS, используя новые возможности Bash, недоступные в версии 3.2. Установив более новую версию Bash через Homebrew (brew install bash
), она будет находиться в /opt/homebrew/bin
, и ваш $PATH
будет настроен так, чтобы сначала искать исполняемые файлы в /opt/homebrew/bin
. Таким образом, использование #!/usr/bin/env bash
будет достаточно для того, чтобы скрипт применял установленную через Homebrew версию Bash.
#!/usr/bin/env bash
echo "Hello from a modern Bash!"
Приложение
1. Использование env
для поиска интерпретатора
Использование #!/usr/bin/env bash
— самый распространенный и рекомендуемый метод из-за его гибкости и универсальности. При условии, что Homebrew установлен и его путь включен в переменную окружения PATH, этот подход автоматически выберет версию Bash, установленную из Homebrew или MacPorts, обеспечивая тем самым выполнение скрипта в более современной среде без необходимости постоянного обновления путей.
Такой метод не лишен недостатков. Если Homebrew-версия Bash недоступна, env
все же выберет устаревший системный Bash. Чтобы смягчить эти риски, рекомендуется четко документировать зависимость скрипта от определенных функций Bash и предлагать инструкцию по установке Homebrew и настройки системы пользователям или клиентам.
2. Создание символических ссылок
Альтернативное решение — создание символических ссылок. Вы можете создать ссылку на современный Bash в /usr/local/bin
, что устранит необходимость изменения shebang в случае изменения способа установки Bash.
ln -s /opt/homebrew/bin/bash /usr/local/bin/bash
Символическая ссылка обеспечит независимость скрипта от специфик местоположения файлов в системе, но все же потребует базового администрирования и может потребовать прав суперпользователя.
3. Исполнение в POSIX-совместимом режиме
И, наконец, если ваш скрипт не критичен к возможностям Bash, рассмотрите использование /bin/sh
для обеспечения максимальной совместимости. Однако этот метод предполагает знание POSIX-совместимых возможностей и ограничений.
Заключение
В профессиональной среде важно, чтобы написанные вами скрипты были как можно более переносимы и адаптируемы под особенности различных систем. Хотя #!/usr/bin/env bash
не лишено недостатков, это все же наилучший выбор для большинства пользователей, стремящихся к сочетаемости между различными операционными системами и окружениями. Это же правило распространяется на создание образов Docker, в которых вы можете контролировать интерпретатор и его версию, еще больше упрощая разработку.
Применяя подобные подходы, вы сможете значительно упростить управление зависимостями ваших проектов и уверенно интегрировать их в разнообразные операционные среды. Обратите внимание на тщательное документирование используемых версий и требований к среде выполнения для облегчения процесса развертывания и сопровождения вашим возможным будущим коллегам или пользователям.