Подстановка переменных
Библиотека env поддерживает ссылки на переменные в конфигурационных файлах для повторного использования конфигурации и динамической подстановки значений.
Включение подстановки переменных
cfg := env.DefaultConfig()
cfg.ExpandVariables = true // Включено по умолчанию
loader, _ := env.New(cfg)
loader.LoadFiles(".env")Базовый синтаксис
Простые ссылки
# Ссылка на другие переменные
BASE_URL=https://api.example.com
API_URL=${BASE_URL}/v1
# API_URL разворачивается в: https://api.example.com/v1
# Краткий синтаксис
HOST=localhost
URL=$HOST:8080
# URL разворачивается в: localhost:8080Синтаксис значений по умолчанию
| Синтаксис | Описание |
|---|---|
${VAR:-default} | Если VAR не существует, используется default |
${VAR:=default} | Если VAR не существует, используется default (аналог :-) |
${VAR:?error} | Если VAR не существует или пусто, возвращается ошибка |
Ограничение самообращения
Переменная, на которую ссылаются :-, :=, :?, должна отличаться от ключа, которому присваивается значение. Самообращение вида KEY=${KEY:-default} распознаётся как цикл и приводит к ошибке ErrExpansionDepth при загрузке. Чтобы задать ключу значение по умолчанию, присвойте литерал напрямую (KEY=default) либо сошлитесь на другую переменную (см. примеры ниже).
Подробный синтаксис
${VAR:-default} - Использование значения по умолчанию
Наиболее распространённый синтаксис значений по умолчанию. Когда переменная не существует, используется значение по умолчанию; если переменная существует (даже с пустым значением), используется оригинальное значение:
# HOST уже определена, используется её значение
HOST=localhost
PRIMARY_HOST=${HOST:-127.0.0.1}
# PRIMARY_HOST разворачивается в: localhost
# Если TIMEOUT не существует, используется значение по умолчанию "30s"
TIMEOUT_VALUE=${TIMEOUT:-30s}
# TIMEOUT_VALUE разворачивается в: 30s
# Вложенные значения по умолчанию
DB_HOST=localhost
DB_URL=${DB_HOST}:${DB_PORT:-5432}
# Если DB_HOST=localhost и DB_PORT не существует
# DB_URL разворачивается в: localhost:5432Сценарии использования:
- Значения по умолчанию для необязательных параметров конфигурации
- Единая конфигурация для сред разработки/продакшена
${VAR:=default} - Использование значения по умолчанию
Поведение идентично ${VAR:-default} — когда переменная не существует, используется значение по умолчанию:
# Если DEBUG не существует, используется "false"
DEBUG_VALUE=${DEBUG:=false}
# Если CACHE_TTL не существует, используется значение по умолчанию
CACHE_TTL_VALUE=${CACHE_TTL:=3600}Связь с :-
${VAR:=default} в данной библиотеке полностью идентична ${VAR:-default} по поведению. Когда переменная не существует, в качестве результата подстановки используется значение по умолчанию. := не записывает значение по умолчанию обратно в хранилище переменных.
${VAR:?error} - Сообщение об ошибке
Если переменная не существует или пуста, возвращается ошибка:
# Если DATABASE_URL не существует, загрузка завершится с ошибкой
DB_URL=${DATABASE_URL:?Database URL is required}
# Если API_TOKEN не существует, будет выдана ошибка
AUTH_TOKEN=${API_TOKEN:?API_TOKEN must be set}Сценарии использования:
- Валидация обязательных параметров конфигурации
- Ранний отказ для предотвращения ошибок во время выполнения
Экранирование
Экранирование знака доллара
Используйте $$ для буквального символа $:
# Конфигурация цены
PRICE=$$99.99
# Разворачивается в: $99.99
# Строка, содержащая $
MESSAGE=Price is $$100
# Разворачивается в: Price is $100Кавычки и подстановка
Подстановка переменных выполняется на едином этапе постобработки после снятия кавычек, поэтому ни одинарные, ни двойные кавычки не влияют на подстановку. Например, SINGLE='${BASE}' (при BASE=hello) после подстановки даёт значение hello — так же, как в двойных кавычках; если ссылочная переменная не определена (например, LITERAL='${NO_EXPANSION}'), результатом будет пустая строка, а не литерал ${NO_EXPANSION}.
Различие между одинарными и двойными кавычками состоит только в буквальном разборе: двойные кавычки обрабатывают escape-последовательности вроде \n, \t, одинарные — сохраняют текст как есть (без экранирования).
Внимание
Не используйте кавычки для «запрета подстановки». Чтобы сохранить литерал ${VAR}, используйте следующие способы:
# Способ 1: экранирование знака доллара ($$ разворачивается в буквальный $)
LITERAL='$${NO_EXPANSION}'
# Значение: ${NO_EXPANSION}// Способ 2: глобальное отключение подстановки переменных
cfg := env.DefaultConfig()
cfg.ExpandVariables = falseВложенная подстановка
Переменные могут содержать вложенные ссылки:
# Базовая конфигурация (избегайте встроенного запрещённого ключа ENV, используйте DEPLOY_ENV)
APP_NAME=myapp
DEPLOY_ENV=production
# Вложенные ссылки
DB_HOST=db.${DEPLOY_ENV}.example.com
# Разворачивается в: db.production.example.com
API_URL=https://${APP_NAME}.${DEPLOY_ENV}.api.example.com
# Разворачивается в: https://myapp.production.api.example.comОбнаружение циклов
Библиотека автоматически обнаруживает циклические ссылки и возвращает ошибку:
# Циклическая ссылка (ошибка)
A=${B}
B=${A}
# При загрузке будет возвращена ошибка ErrExpansionDepthОграничение глубины подстановки
Максимальная глубина подстановки по умолчанию — 5, жёсткий предел — 20:
cfg := env.DefaultConfig()
cfg.MaxExpansionDepth = 10 // Пользовательская глубина| Константа | Значение | Описание |
|---|---|---|
DefaultMaxExpansionDepth | 5 | Значение по умолчанию (публичный API) |
Примечание
Жёсткий предел составляет 20 (внутреннее ограничение). Настроенное значение MaxExpansionDepth не может превышать этот предел.
Полный пример
# Файл .env
# Базовая конфигурация (избегайте встроенного запрещённого ключа ENV)
APP_NAME=myapp
DEPLOY_ENV=development
DEBUG=true
# Конфигурация базы данных
DB_HOST=localhost
DB_PORT=5432
DB_NAME=${APP_NAME}
DB_URL=postgres://${DB_HOST}:${DB_PORT}/${DB_NAME}
# Конфигурация API
API_BASE=https://api.${DEPLOY_ENV}.example.com
API_URL=${API_BASE}/v1
# Конфигурация логирования
LOG_LEVEL=info
# Цена (экранирование)
PRICE=$$99.99package main
import (
"fmt"
"log"
"github.com/cybergodev/env"
)
func main() {
cfg := env.DefaultConfig()
cfg.ExpandVariables = true
loader, err := env.New(cfg)
if err != nil {
log.Fatal(err)
}
defer loader.Close()
err = loader.LoadFiles(".env")
if err != nil {
log.Fatal(err)
}
fmt.Println("DB_URL:", loader.GetString("DB_URL"))
fmt.Println("API_URL:", loader.GetString("API_URL"))
fmt.Println("PRICE:", loader.GetString("PRICE"))
}Связанная документация
- Быстрый старт - Базовое использование
- Config API - Конфигурация ExpandVariables
- Константы и ошибки - Ограничения глубины подстановки