Устранение распространенных синтаксических ошибок Bash: практическое руководство
Исправьте распространенные синтаксические ошибки Bash с примерами для кавычек, скобок, переменных, перенаправлений и проблем поиска команд.
Устранение распространенных синтаксических ошибок Bash: практическое руководство
Синтаксические ошибки Bash обычно возникают из-за мелких оплошностей: пропущенной кавычки, неправильной скобки, неверного перенаправления или переменной, которая развернулась не так, как вы ожидали. Когда ваш скрипт Bash останавливается с сообщением syntax error near unexpected token, начните с проверки строки перед той, на которую указывает Bash, затем выполните синтаксическую проверку скрипта.
Используйте эту быструю проверку перед запуском измененного скрипта на сервере:
bash -n script.sh
bash -n анализирует файл без выполнения команд. Это не выявит все логические ошибки, но поймает многие сломанные кавычки, пропущенные операторы fi и некорректные циклы.
Пропущенные кавычки
Незакрытые кавычки — один из самых быстрых способов запутать парсер.
name="deploy
printf 'Deploying %s\n' "$name"
Bash продолжает читать последующие строки, потому что все еще ищет закрывающую ". Исправьте это, закрыв кавычку и заключив в кавычки расширения переменных, которые могут содержать пробелы:
name="deploy"
printf 'Deploying %s\n' "$name"
Используйте двойные кавычки, когда хотите, чтобы переменные раскрывались. Используйте одинарные кавычки, когда нужен буквальный текст:
printf 'HOME остается буквальным: $HOME\n'
printf "HOME раскрывается: %s\n" "$HOME"
Плохие блоки if, for и while
Каждая составная команда требует своего закрывающего ключевого слова. Его отсутствие часто сообщает об ошибке в конце файла.
if systemctl is-active --quiet nginx; then
echo "nginx is running"
# пропущен fi
Правильная версия:
if systemctl is-active --quiet nginx; then
echo "nginx is running"
fi
Тот же шаблон применяется к циклам и операторам case:
for host in web1 web2 web3; do
ssh "$host" uptime
done
case "$env" in
prod) echo "production" ;;
dev) echo "development" ;;
*) echo "unknown" ;;
esac
Ошибки со скобками и тестами
Команда [ — это реальная команда, поэтому ей нужны пробелы вокруг аргументов и перед закрывающей скобкой.
Сломанный вариант:
if [$count -gt 5]; then
echo "too many"
fi
Исправленный вариант:
if [ "$count" -gt 5 ]; then
echo "too many"
fi
Для скриптов, специфичных для Bash, [[ ... ]] часто безопаснее для строковых тестов, так как более корректно обрабатывает пустые переменные и поддерживает сопоставление с образцом:
if [[ "$file" == *.log ]]; then
gzip "$file"
fi
Используйте числовые операторы для чисел и строковые операторы для текста:
[[ "$status" == "ready" ]] # сравнение строк
[[ "$retries" -lt 3 ]] # числовое сравнение
Проблемы с раскрытием переменных
Распространенная ошибка — добавление пробелов вокруг = при присваивании. Bash воспринимает это как команду, а не как присваивание переменной.
Сломанный вариант:
backup_dir = /var/backups
Исправленный вариант:
backup_dir=/var/backups
Используйте фигурные скобки, когда имя переменной соприкасается с другим текстом:
service="nginx"
log="/var/log/${service}.log"
Без фигурных скобок Bash может прочитать более длинное имя переменной, чем вы предполагали.
Ошибки command not found
command not found не всегда является синтаксической ошибкой. Обычно это означает, что Bash не смог найти исполняемый файл с таким именем.
Сначала проверьте опечатки:
systemctl status nginx
Затем проверьте, существует ли команда и находится ли она в PATH:
command -v systemctl
printf '%s\n' "$PATH"
Если скрипт выполняется через cron, systemd или задание CI, PATH может быть короче, чем в вашей интерактивной оболочке. Используйте абсолютные пути для критических команд или установите PATH в начале скрипта:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
Ошибки перенаправления и конвейеров
Порядок перенаправления имеет значение. Эта команда записывает stdout в app.log, а затем отправляет stderr в то же место:
./deploy.sh >app.log 2>&1
Эта команда отличается, потому что stderr копируется в старый stdout до того, как stdout перенаправляется:
./deploy.sh 2>&1 >app.log
Для конвейеров помните, что Bash обычно возвращает код выхода последней команды. Используйте pipefail, когда сбойная команда в середине должна привести к сбою всего конвейера:
set -o pipefail
kubectl get pods | grep CrashLoopBackOff
Практический процесс отладки
Начните с синтаксической проверки:
bash -n script.sh
Запустите с трассировкой, когда синтаксис корректен, но поведение неверно:
bash -x script.sh
Для более безопасных производственных скриптов добавляйте строгий режим намеренно и тестируйте скрипт после каждого изменения:
set -euo pipefail
set -e завершает работу при многих сбоях команд, set -u рассматривает неопределенные переменные как ошибки, а pipefail перехватывает сбои внутри конвейеров. Эти параметры полезны, но они могут изменить поведение скрипта, поэтому включайте их осознанно, а не вставляйте в старый скрипт без тестирования.
Вывод
Большинство синтаксических ошибок Bash становятся простыми, если проверять кавычки, закрывающие ключевые слова, пробелы вокруг скобок, присваивания переменных и перенаправления в таком порядке. Держите bash -n и bash -x в своем обычном рабочем процессе и тестируйте скрипты в той же оболочке и среде, которые будут использоваться в производстве.