shellcheck
Статический анализатор shell-скриптов — находит ошибки с кавычками, условиями и переносимостью
shellcheck читает скрипт, не запуская его, и указывает на типичные ошибки: переменные без кавычек, cd без проверки, цикл по выводу ls, bash-конструкции в #!/bin/sh. У каждого предупреждения есть код SCxxxx — по нему на shellcheck.net/wiki есть подробное объяснение с примером исправления.
Синтаксис
shellcheck [опции] скрипт…
Диалект берётся из shebang (#!/bin/bash, #!/bin/sh) или из опции -s. Код возврата: 0 — замечаний нет, 1 — есть замечания, 2 — файл не прочитан, 3 и 4 — ошибка в опциях.
Опции
| Опция | Что делает |
|---|---|
-s bash / -s sh / -s dash / -s ksh | диалект, если в файле нет shebang |
-S error / warning / info / style | показывать только замечания этого уровня и серьёзнее |
-e SC2086,SC2034 | исключить указанные коды |
-i SC2086 | показывать только указанные коды |
-o all / -o имя | включить необязательные проверки (по умолчанию выключены) |
--list-optional | список необязательных проверок с примерами |
-x | заходить в файлы, подключённые через source / . |
-P путь | где искать подключаемые файлы (SCRIPTDIR — каталог скрипта) |
-a | показывать замечания и по подключённым файлам |
-f tty | формат по умолчанию: строка кода, стрелка, объяснение, «Did you mean» |
-f gcc | одна строка на замечание: файл:строка:столбец: уровень: текст [SCxxxx] |
-f diff | исправления в виде патча (shellcheck -f diff s.sh | patch -p1) |
-f json1 / -f checkstyle | машиночитаемый вывод для CI |
-f quiet | ничего не печатать, только код возврата |
--color=never | без цветов (у -C значение пишется слитно: -Cnever) |
-W 0 | не печатать ссылки на wiki в конце |
--norc | не читать .shellcheckrc |
- вместо файла | читать скрипт со stdin |
Уровни
| Уровень | Что обычно означает |
|---|---|
error | почти наверняка ошибка: синтаксис, цикл по $(ls), [[ в sh |
warning | вероятный баг: cd без проверки, переменная не задана или не используется |
info | стоит исправить: кавычки, read без -r |
style | стиль: $(…) вместо обратных кавычек, [[ ]] вместо [ ] |
Директивы в скрипте
| Комментарий | Что делает |
|---|---|
# shellcheck disable=SC2086 | отключить код для следующей команды (или блока { … }, функции, цикла) |
| то же сразу после shebang, до первой команды | отключить для всего файла |
# shellcheck disable=SC2086,SC2154 | несколько кодов через запятую |
# shellcheck source=lib.sh | какой файл на самом деле подключает source "$dir/lib.sh" |
# shellcheck source=/dev/null | не пытаться читать подключаемый файл |
# shellcheck shell=bash | задать диалект внутри файла без shebang |
Файл .shellcheckrc в каталоге проекта или в домашнем каталоге задаёт те же директивы для всех скриптов: disable=SC2086, enable=all, shell=bash.
Частые коды
| Код | Суть |
|---|---|
SC2086 | переменная без кавычек будет разбита на слова и раскрыта как шаблон |
SC2164 | cd без || exit: если каталога нет, команды выполнятся не там |
SC2045 | for f in $(ls …) ломается на пробелах — используй glob |
SC2154 | переменная используется, но нигде не присвоена |
SC2034 | переменная присвоена, но не используется |
SC2162 | read без -r портит обратные слэши |
SC2155 | local x=$(cmd) прячет код возврата cmd |
SC2148 | нет shebang — непонятно, какой это диалект |
SC1091 | подключаемый файл не прочитан (нужен -x) |
SC3010 и другие SC3xxx | bash-конструкция в POSIX sh |
Примеры
# проверить скрипт
shellcheck deploy.sh
# компактный вывод — удобно для grep и редакторов
shellcheck -f gcc deploy.sh
# только ошибки и предупреждения, без info и style
shellcheck -S warning deploy.sh
# скрипт без shebang
shellcheck -s bash script
# проверить все .sh в проекте
find . -name '*.sh' -exec shellcheck {} +
# посмотреть предлагаемые исправления патчем
shellcheck -f diff deploy.sh
# в CI: упасть, если есть хоть одно замечание
shellcheck -f quiet *.sh || exit 1
# проверить кусок кода со stdin
echo 'echo $1' | shellcheck -s bash -
Подводные камни
shellcheckне запускает скрипт. Он не знает, существуют ли файлы и команды, и не найдёт логических ошибок. Чистый вывод не значит «скрипт работает».- Без shebang почти все проверки диалекта отключены, а первым замечанием будет
SC2148. Ставь#!/bin/bashили передавай-s. #!/bin/sh— это не bash.[[ ]], массивы,&>дадутSC3xxx. Это правда: в Ubuntush— этоdash, и такой скрипт сломается.- Директива действует на следующую команду, а не на строку. Комментарий в конце строки (
echo $x # shellcheck disable=…) — ошибка разбораSC1126. А директива сразу после shebang, перед первой командой, отключает проверку во всём файле. disableглушит симптом. Прежде чем отключатьSC2086, убедись, что разбиение на слова действительно нужно.- Подключаемые файлы по умолчанию не читаются: переменные из них покажутся неприсвоенными (
SC2154). Добавь-x.