Кавычки, подстановки и 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[@]}". Объясните число аргументов в каждом случае.
Что важно запомнить
- Программа получает уже разобранные аргументы; кавычки управляют работой shell до запуска.
- Двойные кавычки сохраняют значение одним аргументом, одинарные отключают подстановки.
- Glob сопоставляет имена файлов и не заменяет регулярные выражения.