Кавычки, подстановки и glob: как shell разбирает команду

Shell сначала разбирает командную строку, выполняет подстановки и только затем запускает программу с готовым массивом аргументов. Большинство загадочных ошибок с пробелами, звёздочками и переменными возникает до старта самой утилиты.

Что меняют кавычки

Одинарные кавычки сохраняют все символы буквально. Двойные разрешают подстановку переменных и команд, но удерживают результат одним аргументом. Без кавычек shell может разделить значение по пробелам и дополнительно раскрыть glob-шаблоны.

project_name='demo project'
printf '<%s>\n' "$project_name"
printf '<%s>\n' '$project_name'
printf '<%s>\n' $project_name

Первая команда получает один аргумент demo project, вторая — буквальный текст $project_name, третья обычно получает два аргумента. Такое случайное дробление особенно опасно для путей и списков, полученных из внешних данных.

Glob не является регулярным выражением

Шаблон *.log раскрывает сам shell в список существующих имён текущего каталога. Точка означает обычную точку, а * — любую последовательность символов. Скрытые имена, начинающиеся с точки, по умолчанию не совпадают с обычной звёздочкой.

mkdir -p /tmp/glob-demo
touch '/tmp/glob-demo/app one.log' /tmp/glob-demo/app-two.log
printf '%s\n' /tmp/glob-demo/*.log

Каждый найденный путь передаётся отдельным аргументом; пробел внутри имени не делит его повторно после pathname expansion. Но если совпадений нет, Bash без nullglob может оставить шаблон как буквальную строку, и программа попробует открыть файл со звёздочкой в имени.

Подстановка команды

$(command) заменяется stdout вложенной команды без завершающих переводов строки. Результат почти всегда следует заключать в двойные кавычки.

current_branch=$(git branch --show-current)
printf 'branch=%s\n' "$current_branch"

Не используйте результат подстановки как готовую команду. Строка не является безопасным массивом аргументов; для сложного вызова сформируйте Bash-массив и передайте его через "${args[@]}".

Ошибка и диагностика

Симптом «команда работает вручную, но ломается на имени с пробелом» почти всегда требует посмотреть реальные аргументы. Добавьте printf '<%q>\n' ... или временно включите set -x; Bash покажет команды после раскрытия. Не исправляйте проблему добавлением eval: он повторно интерпретирует строку и способен превратить данные в код.

Практика

Создайте файлы report one.txt, report-two.txt и .hidden.txt. Сравните вывод quoted и unquoted переменной с путём, glob *.txt и буквальный шаблон '*.txt'. Затем соберите массив из двух путей и передайте его в printf '<%s>\n' "${files[@]}". Объясните число аргументов в каждом случае.

Что важно запомнить

Источники