Как динамически получить имя файла Bash‑скрипта
Узнайте несколько способов динамически получить имя текущего Bash‑скрипта без жёсткого кодирования. Сравните $0, BASH_SOURCE и методы разрешения симлинков.
Как определить имя текущего файла Bash‑скрипта внутри самого скрипта?
Например, если мой скрипт находится в файле с именем runme.sh, как вывести сообщение вроде «You are running runme.sh» без жёстко заданного имени файла?
Наиболее распространённый способ получить имя текущего скрипта в Bash — использовать $0 в сочетании с командой basename или расширением параметров Bash. Например, basename "$0" или ${0##*/} вернёт только имя файла без пути, что позволяет выводить сообщения вроде «Вы запускаете runme.sh» без жёсткого кодирования.
Содержание
- Базовые методы получения имени скрипта
- Сравнение подходов $0 и BASH_SOURCE
- Обработка симлинков и сложных сценариев
- Полные примеры
- Лучшие практики и рекомендации
Базовые методы получения имени скрипта
Использование $0 с basename
Самый простой способ — использовать специальный параметр $0 в сочетании с командой basename. Согласно Mozilla Developer Network, $0 содержит имя скрипта или оболочки, как оно было вызвано.
#!/bin/bash
echo "Вы запускаете $(basename "$0")"
Вывод будет: Вы запускаете runme.sh
Использование расширения параметров Bash
Bash предоставляет расширение параметров, которое позволяет добиться того же результата без вызова внешних команд:
#!/bin/bash
script_name="${0##*/}"
echo "Вы запускаете $script_name"
Паттерн ${0##*/} удаляет всё до и включая последний / из значения $0, оставляя только имя файла.
Обработка имён файлов с пробелами
При работе со скриптами, имена которых содержат пробелы, важно правильно экранировать:
Согласно Stack Overflow, «Если имя скрипта содержит пробелы, более надёжный способ — использовать
"$0"или$(basename "$0")– или на MacOS:$(basename \"$0\"). Это предотвращает искажение имени или его неправильную интерпретацию.»
#!/bin/bash
echo "Вы запускаете '$(basename "$0")'"
Сравнение подходов $0 и BASH_SOURCE
Когда использовать $0
$0 хорошо работает для напрямую исполняемых скриптов, но имеет ограничения:
- Возвращает имя, как оно было использовано при вызове (может быть относительным или абсолютным путём)
- Может работать некорректно, если скрипт подключён через
source - Может быть затронут симлинками
Когда использовать BASH_SOURCE
Массив BASH_SOURCE более надёжен в сложных сценариях:
Исследования из Stack Overflow показывают, что «
${BASH_SOURCE[0]}содержит путь к файлу скрипта, в котором определена эта функция.»
#!/bin/bash
echo "Вы запускаете $(basename "${BASH_SOURCE[0]}")"
Ключевые различия
| Сценарий | $0 | BASH_SOURCE[0] |
|---|---|---|
| Прямой запуск | Работает корректно | Работает корректно |
| Подключённый скрипт | Может показать bash |
Показывает реальный скрипт |
| Функция внутри скрипта | Показывает имя функции | Показывает оригинальный скрипт |
| Разрешение симлинков | Показывает путь симлинка | Показывает путь симлинка |
Обработка симлинков и сложных сценариев
Разрешение симлинков
Если ваш скрипт может быть вызван через симлинк, вы захотите разрешить реальный файл:
#!/bin/bash
# Получаем реальный файл скрипта, разрешая симлинки
actual_script=$(readlink -f "$0")
script_name=$(basename "$actual_script")
echo "Вы запускаете $script_name"
echo "Полный путь: $actual_script"
Получение директории скрипта
Часто нужно как имя файла, так и директорию:
#!/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")"), чтобы получить директорию, содержащую скрипт, чтобы справиться с ситуацией, когда имя скрипта само является симлинком.»
Определение, подключён ли скрипт или исполняется напрямую
Вы можете определить, подключается ли ваш скрипт или исполняется напрямую:
#!/bin/bash
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
echo "Скрипт $(basename "${BASH_SOURCE[0]}") подключён"
else
echo "Скрипт $(basename "$0") исполняется напрямую"
fi
Полные примеры
Пример 1: Простой скрипт логирования
#!/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: Надёжный скрипт с разрешением пути
#!/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: Скрипт, учитывающий контекст функции
#!/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
Лучшие практики и рекомендации
Выберите подходящий метод для вашего сценария
- Для простого прямого запуска:
basename "$0"достаточно - Для подключённых скриптов: используйте
basename "${BASH_SOURCE[0]}" - Для разрешения симлинков: применяйте
readlink -f "$0" - Для максимальной надёжности: комбинируйте несколько подходов
Всегда экранируйте переменные
Согласно Baeldung on Linux, правильное экранирование предотвращает проблемы с пробелами и специальными символами в именах файлов.
# Хорошо
echo "Запуск '$(basename "$0")'"
# Плохо – может не сработать с пробелами
echo "Запуск $(basename $0)"
Обрабатывайте крайние случаи
Будьте внимательны к следующим сценариям:
- Скрипты с пробелами в именах
- Скрипты, выполняемые через симлинки
- Подключённые скрипты vs исполняемые скрипты
- Скрипты, вызываемые с относительными vs абсолютными путями
- Скрипты, вызываемые из разных каталогов
Полное надёжное решение
Для максимальной надёжности используйте следующий подход:
#!/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"
Этот комплексный подход обрабатывает большинство крайних случаев и обеспечивает последовательные результаты в различных сценариях выполнения.
Источники
- How do I know the script file name in a Bash script? - Stack Overflow
- Find the Script’s Filename Within the Same Script in Bash | Baeldung on Linux
- Getting current script location in bash (like python file variable) - Unix Stack Exchange
- bash - choosing between $0 and BASH_SOURCE - Stack Overflow
- How to detect if a script is being sourced - Stack Overflow
- BashFAQ/028 - Greg’s Wiki
- 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 обеспечат лучшие результаты. Не забывайте тестировать ваш скрипт в различных сценариях, чтобы убедиться в корректной работе.