# Секрет первой строки скрипта Linux: Что это за `#!/usr/bin/env bash` и `#!/bin/bash`? При создании скриптов в Linux принято по умолчанию добавлять в самом начале одну из этих строк: ```bash #!/usr/bin/env bash ``` или ```bash #!/bin/bash ``` На первый взгляд они похожи на комментарии, но каково их **истинное предназначение**? И в чем разница между ними? В этой статье мы разберемся в значении этих строк и выясним, когда какую из них следует использовать. --- ## 1. Это не комментарий: Что такое shebang (шебанг)? {#sec-9f8041c620ba} Строка, начинающаяся с `#!`, называется **shebang (шебанг)**. ```bash #!/bin/bash ``` Для оболочки (shell) строка, начинающаяся с `#`, выглядит как обычный комментарий. Однако для **операционной системы (ядра)** это не комментарий, а: > «Директива, указывающая, где находится **программа-интерпретатор**, которая должна выполнить этот скрипт». То есть: * `#!/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`, что исключает процесс поиска по путям. * Во многих дистрибутивах Linux `/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, то будет использоваться именно эта версия. 3. **Тот же шаблон используется для Python и других языков** ```python #!/usr/bin/env python3 ``` ### Недостатки {#sec-0ba7b1b3de08} 1. **Предполагается наличие `/usr/bin/env`** * Он присутствует практически во всех современных системах Unix/Linux, но в очень специфических окружениях его может не быть. 2. **Возможно использование другого интерпретатора в зависимости от PATH** * Если настройки PATH некорректны или неожиданный bash оказывается первым в списке, может быть запущена нежелательная версия. 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 ``` * Причины: * Расположение bash может отличаться на серверах, в локальных средах, CI-средах, управляемых разработчиками. * Поиск и выполнение на основе 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 ``` * Однако, если вы хотите запускать скрипт так: ```bash ./myscript.sh ./myscript.py ``` * то shebang абсолютно необходим. Особенно, если это «инструментальный» скрипт, который могут использовать другие, **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의 작동원리이미지")