# Секрет первой строки в скриптах [[Линукс]]: Что такое `#!/usr/bin/env bash` и `#!/bin/bash`? Когда вы пишете скрипты в [[Линукс]], то по привычке добавляете в самом начале одну из этих строк: ```bash #!/usr/bin/env bash ``` или ```bash #!/bin/bash ``` На первый взгляд, это похоже на обычный комментарий… Но что на самом деле скрывается за этой строкой? И в чем заключаются их различия? В этой статье мы подробно разберем значение этой строки и выясним, когда какой вариант следует использовать. --- ## 1. Это не комментарий: Что такое shebang? {#sec-9f8041c620ba} Строка, начинающаяся с `#!`, называется **shebang**. ```bash #!/bin/bash ``` Для оболочки это выглядит как комментарий, поскольку она начинается с `#`. Однако для **операционной системы (ядра)** это не комментарий, а: > "Директива, указывающая, где находится **программа-интерпретатор**, которая должна выполнить этот скрипт." То есть: * `#!/bin/bash` → "Выполни этот файл с помощью `/bin/bash`." * `#!/usr/bin/env bash` → "Найди интерпретатор `bash` через `env` и выполни этот файл с его помощью." Вот такое значение имеет эта строка. --- ## 2. Как ядро выполняет скрипты? {#sec-33d96b8d8f01} Если сильно упростить процесс выполнения, то он выглядит так: 1. Пользователь запускает скрипт с правами на исполнение. ```bash chmod +x script.sh ./script.sh ``` 2. Ядро считывает `script.sh`. 3. Проверяет, являются ли **первые два символа** файла `#!`. 4. Если да, то оставшаяся часть строки * интерпретируется как "путь к интерпретатору + аргументы" * и запускает эту программу, передавая ей **путь к файлу скрипта в качестве аргумента**. Например, если первая строка `script.sh` выглядит так: ```bash #!/bin/bash ``` Тогда ядро фактически выполняет что-то вроде: ```bash /bin/bash script.sh ``` То есть, ядро выполняет действие, аналогичное тому, как если бы мы сами запустили `bash script.sh`. > Примечание: > Перед `#!/` **не должно быть пробелов**. > **Первым символом** файла должен быть `#`, а вторым — `!`. --- ## 3. Значение и особенности `#!/bin/bash` {#sec-52f9e12dce0d} Это наиболее часто встречающаяся форма. ```bash #!/bin/bash ``` ### Значение {#sec-a97a945792d4} * "Этот скрипт является **bash-скриптом**, и `bash` находится по пути `/bin/bash`." * При выполнении этого скрипта ядро всегда запускает `/bin/bash`. ### Преимущества {#sec-a01bf708579d} * **Ясность**: Всегда используется `/bin/bash`, что делает предсказуемым, какая версия bash будет применена. * **Производительность/Простота**: Выполняется напрямую, без обращения к `env`, что исключает процесс поиска пути. * Многие дистрибутивы [[Линукс]] рассматривают `/bin/bash` фактически как "стандартное расположение". ### Недостатки {#sec-89064c01f1ea} * **Может быть непереносимым** * В некоторых системах bash может находиться по другому пути, например, `/usr/bin/bash`, `/usr/local/bin/bash` и т.д. * В некоторых системах bash может быть вообще не установлен, и может присутствовать только `/bin/sh`. * В частности, пути могут отличаться в **macOS, системах на базе BSD, NixOS, некоторых контейнерных средах** и т.д. --- ## 4. Значение и особенности `#!/usr/bin/env bash` {#sec-97cd6f02458d} Сегодня эта форма часто встречается в скриптах. ```bash #!/usr/bin/env bash ``` Ключевым моментом здесь является `/usr/bin/env`. * `env` — это утилита, которая помогает устанавливать/проверять переменные среды и искать программы в `PATH`. * Можно считать, что ядро фактически выполняет это так: ```bash /usr/bin/env bash script.sh ``` * `env` просматривает системную переменную среды `PATH` и находит исполняемый файл `bash`, а затем запускает его. ### Преимущества {#sec-9bc2075d78b3} 1. **Портативность (хорошо работает в различных средах)** * Независимо от того, находится ли bash в `/bin/bash`, `/usr/bin/bash` или `/usr/local/bin/bash`, * `env` найдет его, если он правильно зарегистрирован в `PATH`. 2. **Использование bash, соответствующего пользовательской среде** * Если пользователь настроил `PATH` так, чтобы использовать определенную версию bash в первую очередь, то именно эта версия bash будет использоваться. 3. **Используется тот же шаблон для Python и других языков** ```sh #!/usr/bin/env python3 ``` ### Недостатки {#sec-0ba7b1b3de08} 1. **Предполагается наличие `/usr/bin/env`** * Он присутствует практически во всех современных Unix/[[Линукс]] системах, но в очень специфических средах может отсутствовать. 2. **В зависимости от PATH может быть выбран другой интерпретатор** * Если настройка PATH нарушена или неожиданная версия bash находится раньше в PATH, может быть выполнена нежелательная версия. 3. **Иногда требует осторожности с точки зрения безопасности** * В очень чувствительных к безопасности средах предпочтение отдается **абсолютным путям**, а не поиску интерпретатора на основе PATH. --- ## 5. Сравнение двух подходов {#sec-f327651f1197} Представим краткую сравнительную таблицу: | Критерий | #!/bin/bash | #!/usr/bin/env bash | |---|---|---| | Метод указания пути к интерпретатору | Фиксированный абсолютный путь | `PATH` через поиск | | Портативность (совместимость с разными системами) | Низкая (ломается при изменении пути) | Высокая (достаточно, чтобы bash был в PATH) | | Какая версия bash используется | Всегда `/bin/bash` | Первая найденная `bash` в PATH | | Гарантия нужной версии | Относительно легко | Может меняться в зависимости от состояния PATH | | Безопасность/Контроль | Выше (фиксированный путь) | Немного ниже (зависит от PATH) | | Современные тенденции | Относительно устаревший стиль | В последнее время чаще рекомендуется | --- ## 6. Когда что использовать? {#sec-a9bac1ab9ba4} "В итоге, что мне использовать?" — ответим на этот вопрос, разобрав ситуации. ### 1) Скрипты для личного или командного использования (типичная среда разработки) {#sec-fc5216000f29} * В основном рекомендуется следующее: ```bash #!/usr/bin/env bash ``` * Причина: * В серверах, локальных средах, CI-средах, управляемых разработчиками, расположение bash может отличаться. * Поиск и выполнение на основе PATH более гибок, и современные инструменты/скрипты предпочитают этот подход. ### 2) Операционные скрипты, строго соответствующие конкретной серверной среде {#sec-204ac6ba64d3} * Например, если на всех серверах компании bash установлен по общему пути `/bin/bash`, * И среда сервера достаточно статична: ```bash #!/bin/bash ``` * Причина: * Гарантирует использование всегда одного и того же интерпретатора. * Помогает избежать непредвиденного поведения, вызванного изменениями PATH. ### 3) Если вы хотите сделать скрипт максимально переносимым? {#sec-6ef1b12d728e} * Если вы предполагаете среду, где "bash может вообще отсутствовать", то стоит задуматься, действительно ли скрипт должен зависеть от bash. * Если возможно, пишите на `sh`: ```bash #!/bin/sh ``` * Этот вариант будет работать в гораздо более широком спектре сред. Однако в этом случае нельзя использовать специфический синтаксис bash (`[[ ]]`, массивы, расширенная обработка строк и т.д.). --- ## 7. Практические советы при использовании `#!/usr/bin/env bash` {#sec-e5a739214684} ### 1) Как запускать скрипты {#sec-83e5cc5b64a5} Чтобы shebang работал правильно, не просто делайте так: ```bash bash script.sh # ← При таком запуске shebang практически не имеет смысла ``` Лучше использовать следующий способ: ```bash chmod +x script.sh # Предоставить права на исполнение ./script.sh # Запустить напрямую ``` Только так ядро прочитает `#!` и использует указанный интерпретатор. ### 2) Проверка передачи аргументов {#sec-90ca3c9235bf} Предположим, мы создали `test.sh` следующим образом: ```bash #!/usr/bin/env bash echo "Интерпретатор: $0" echo "Аргументы: $@" ``` Запуск: ```bash chmod +x test.sh ./test.sh hello world ``` Вывод: ```text Интерпретатор: ./test.sh Аргументы: hello world ``` Здесь `$0` — это "путь к самому файлу скрипта", и достаточно помнить, что ядро фактически выполняет его как `/usr/bin/env bash test.sh hello world`. --- ## 8. Часто задаваемые вопросы {#sec-99b0325925f3} ### Я видел скрипты, написанные без shebang. Это нормально? {#sec-751234a46d29} * Верно, **он не всегда обязателен**. * Если вы запускаете скрипт, явно указывая интерпретатор, как показано ниже, shebang не нужен. Даже если он присутствует, он будет проигнорирован. ```bash bash myscript.sh python3 myscript.py ``` * Однако, если вы хотите запустить скрипт следующим образом, shebang обязателен. ```bash ./myscript.sh ./myscript.py ``` * Особенно, если это "утилитарный" скрипт, предназначенный для использования другими, **shebang почти всегда необходим**. ### "Я слышал, что `#!/bin/env` тоже работает, а не только `#!/usr/bin/env`?" {#sec-6de89a827da4} * В некоторых системах `/bin/env` действительно может существовать. * Однако `/usr/bin/env` обычно является гораздо более универсальным стандартным расположением. * Если нет особых причин, безопаснее использовать `#!/usr/bin/env …`. --- ## 9. Итог {#sec-971578a5a9d1} * `#!/usr/bin/env bash` или `#!/bin/bash` — это **не комментарий, а директива, указывающая ядру, какой интерпретатор использовать для выполнения скрипта**. * `#!/bin/bash` * Всегда использует `/bin/bash` → **предсказуемо в фиксированных средах, но может быть непереносимым**. * `#!/usr/bin/env bash` * Ищет bash в `PATH` → **более гибок и переносим, но зависит от состояния PATH**. * В общих средах разработки/развертывания для "современного" написания скриптов рекомендуется **использовать `#!/usr/bin/env bash` в качестве значения по умолчанию**. ![Принцип работы shebang в Linux-скриптах](https://blog.mikihands.com/media/editor_temp/6/692ed85d-0eb6-4fd1-ab7a-22a2d189175d.png "Принцип работы shebang")