Другое

Как динамически получить имя файла Bash‑скрипта

Узнайте несколько способов динамически получить имя текущего Bash‑скрипта без жёсткого кодирования. Сравните $0, BASH_SOURCE и методы разрешения симлинков.

Как определить имя текущего файла Bash‑скрипта внутри самого скрипта?
Например, если мой скрипт находится в файле с именем runme.sh, как вывести сообщение вроде «You are running runme.sh» без жёстко заданного имени файла?

Наиболее распространённый способ получить имя текущего скрипта в Bash — использовать $0 в сочетании с командой basename или расширением параметров Bash. Например, basename "$0" или ${0##*/} вернёт только имя файла без пути, что позволяет выводить сообщения вроде «Вы запускаете runme.sh» без жёсткого кодирования.

Содержание

Базовые методы получения имени скрипта

Использование $0 с basename

Самый простой способ — использовать специальный параметр $0 в сочетании с командой basename. Согласно Mozilla Developer Network, $0 содержит имя скрипта или оболочки, как оно было вызвано.

bash
#!/bin/bash
echo "Вы запускаете $(basename "$0")"

Вывод будет: Вы запускаете runme.sh

Использование расширения параметров Bash

Bash предоставляет расширение параметров, которое позволяет добиться того же результата без вызова внешних команд:

bash
#!/bin/bash
script_name="${0##*/}"
echo "Вы запускаете $script_name"

Паттерн ${0##*/} удаляет всё до и включая последний / из значения $0, оставляя только имя файла.

Обработка имён файлов с пробелами

При работе со скриптами, имена которых содержат пробелы, важно правильно экранировать:

Согласно Stack Overflow, «Если имя скрипта содержит пробелы, более надёжный способ — использовать "$0" или $(basename "$0") – или на MacOS: $(basename \"$0\"). Это предотвращает искажение имени или его неправильную интерпретацию.»

bash
#!/bin/bash
echo "Вы запускаете '$(basename "$0")'"

Сравнение подходов $0 и BASH_SOURCE

Когда использовать $0

$0 хорошо работает для напрямую исполняемых скриптов, но имеет ограничения:

  • Возвращает имя, как оно было использовано при вызове (может быть относительным или абсолютным путём)
  • Может работать некорректно, если скрипт подключён через source
  • Может быть затронут симлинками

Когда использовать BASH_SOURCE

Массив BASH_SOURCE более надёжен в сложных сценариях:

Исследования из Stack Overflow показывают, что «${BASH_SOURCE[0]} содержит путь к файлу скрипта, в котором определена эта функция.»

bash
#!/bin/bash
echo "Вы запускаете $(basename "${BASH_SOURCE[0]}")"

Ключевые различия

Сценарий $0 BASH_SOURCE[0]
Прямой запуск Работает корректно Работает корректно
Подключённый скрипт Может показать bash Показывает реальный скрипт
Функция внутри скрипта Показывает имя функции Показывает оригинальный скрипт
Разрешение симлинков Показывает путь симлинка Показывает путь симлинка

Разрешение симлинков

Если ваш скрипт может быть вызван через симлинк, вы захотите разрешить реальный файл:

bash
#!/bin/bash
# Получаем реальный файл скрипта, разрешая симлинки
actual_script=$(readlink -f "$0")
script_name=$(basename "$actual_script")
echo "Вы запускаете $script_name"
echo "Полный путь: $actual_script"

Получение директории скрипта

Часто нужно как имя файла, так и директорию:

bash
#!/bin/bash
# Получаем директорию скрипта с разрешением симлинков
script_dir=$(dirname "$(readlink -f "$0")")
script_name=$(basename "$0")

echo "Директория скрипта: $script_dir"
echo "Имя скрипта: $script_name"

Как отмечено в Unix Stack Exchange, «Вы должны использовать $(dirname "$(readlink -f "$0")"), чтобы получить директорию, содержащую скрипт, чтобы справиться с ситуацией, когда имя скрипта само является симлинком.»

Определение, подключён ли скрипт или исполняется напрямую

Вы можете определить, подключается ли ваш скрипт или исполняется напрямую:

bash
#!/bin/bash
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
    echo "Скрипт $(basename "${BASH_SOURCE[0]}") подключён"
else
    echo "Скрипт $(basename "$0") исполняется напрямую"
fi

Полные примеры

Пример 1: Простой скрипт логирования

bash
#!/bin/bash
# logme.sh - простой скрипт логирования, который идентифицирует себя

log_message() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] $(basename "$0"): $1"
}

log_message "Скрипт запущен"
log_message "Обрабатываются файлы..."
log_message "Скрипт завершён"

Пример 2: Надёжный скрипт с разрешением пути

bash
#!/bin/bash
# robust_script.sh - обрабатывает различные сценарии выполнения

# Получаем информацию о скрипте с разрешением симлинков
get_script_info() {
    local script_path=$(readlink -f "${BASH_SOURCE[0]}")
    local script_dir=$(dirname "$script_path")
    local script_name=$(basename "$script_path")
    
    echo "Имя скрипта: $script_name"
    echo "Директория скрипта: $script_dir"
    echo "Полный путь: $script_path"
    
    # Проверяем, подключён ли скрипт
    if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
        echo "Статус: подключён"
    else
        echo "Статус: исполняется напрямую"
    fi
}

# Основное выполнение
get_script_info

# Пример использования
echo "Запуск $(basename "${BASH_SOURCE[0]}") из $(dirname "${BASH_SOURCE[0]}")"

Пример 3: Скрипт, учитывающий контекст функции

bash
#!/bin/bash
# function_demo.sh - показывает имя скрипта в разных контекстах

main_function() {
    echo "В основной функции скрипт: $(basename "${BASH_SOURCE[0]}")"
    nested_function
}

nested_function() {
    echo "В вложенной функции скрипт: $(basename "${BASH_SOURCE[0]}")"
    echo "Имя функции: ${FUNCNAME[0]}"
}

# Проверяем, как мы вызываем скрипт
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    echo "Прямой запуск $(basename "$0")"
    main_function
else
    echo "Подключён $(basename "${BASH_SOURCE[0]}")"
    main_function
fi

Лучшие практики и рекомендации

Выберите подходящий метод для вашего сценария

  1. Для простого прямого запуска: basename "$0" достаточно
  2. Для подключённых скриптов: используйте basename "${BASH_SOURCE[0]}"
  3. Для разрешения симлинков: применяйте readlink -f "$0"
  4. Для максимальной надёжности: комбинируйте несколько подходов

Всегда экранируйте переменные

Согласно Baeldung on Linux, правильное экранирование предотвращает проблемы с пробелами и специальными символами в именах файлов.

bash
# Хорошо
echo "Запуск '$(basename "$0")'"

# Плохо – может не сработать с пробелами
echo "Запуск $(basename $0)"

Обрабатывайте крайние случаи

Будьте внимательны к следующим сценариям:

  • Скрипты с пробелами в именах
  • Скрипты, выполняемые через симлинки
  • Подключённые скрипты vs исполняемые скрипты
  • Скрипты, вызываемые с относительными vs абсолютными путями
  • Скрипты, вызываемые из разных каталогов

Полное надёжное решение

Для максимальной надёжности используйте следующий подход:

bash
#!/bin/bash
# Получаем имя скрипта с несколькими резервными методами

get_script_name() {
    # Сначала пробуем BASH_SOURCE (работает для подключённых скриптов)
    if [[ -n "${BASH_SOURCE[0]}" ]]; then
        local name=$(basename "${BASH_SOURCE[0]}")
        if [[ -n "$name" ]]; then
            echo "$name"
            return 0
        fi
    fi
    
    # Если не удалось, используем $0
    if [[ -n "$0" ]]; then
        local name=$(basename "$0")
        if [[ -n "$name" ]]; then
            echo "$name"
            return 0
        fi
    fi
    
    # Финальный резерв
    echo "unknown_script"
}

script_name=$(get_script_name)
echo "Вы запускаете $script_name"

Этот комплексный подход обрабатывает большинство крайних случаев и обеспечивает последовательные результаты в различных сценариях выполнения.

Источники

  1. How do I know the script file name in a Bash script? - Stack Overflow
  2. Find the Script’s Filename Within the Same Script in Bash | Baeldung on Linux
  3. Getting current script location in bash (like python file variable) - Unix Stack Exchange
  4. bash - choosing between $0 and BASH_SOURCE - Stack Overflow
  5. How to detect if a script is being sourced - Stack Overflow
  6. BashFAQ/028 - Greg’s Wiki
  7. Get path of current script when executed through a symlink - Unix Stack Exchange

Заключение

Получить имя текущего скрипта Bash без жёсткого кодирования легко, если использовать правильный подход. Ключевые выводы:

  • Для простого прямого запуска используйте basename "$0"
  • Для более надёжной обработки, особенно при подключении скриптов, применяйте basename "${BASH_SOURCE[0]}"
  • При необходимости разрешения симлинков используйте readlink -f "$0"
  • Правильное экранирование ("$0" вместо $0) критично для имён файлов с пробелами
  • Рассмотрите комбинирование нескольких подходов для максимальной надёжности в сложных сценариях

Для большинства случаев basename "$0" будет работать без проблем. Однако при работе с подключёнными скриптами, симлинками или сложными средами выполнения более надёжные подходы с использованием BASH_SOURCE и readlink -f обеспечат лучшие результаты. Не забывайте тестировать ваш скрипт в различных сценариях, чтобы убедиться в корректной работе.

Авторы
Проверено модерацией
Модерация