С чего начать Первые шаги трек · 15 ступеней
Linux Labs

shellcheck — проверка скриптов до запуска

6 мин Ubuntu 24.04 · ShellCheck 0.9

shellcheck читает скрипт bash или sh, не выполняя его, и находит ошибки: переменные без кавычек, cd без проверки.

Шпаргалка

8 команд Версия для печати
  • shellcheck backup.sh проверить скрипт
  • shellcheck *.sh несколько файлов сразу
  • shellcheck -f gcc backup.sh одно замечание — одна строка
  • shellcheck -S warning backup.sh только warning и error
  • shellcheck -e SC2034 backup.sh не показывать этот код
  • shellcheck -x backup.sh читать файлы из source
  • shellcheck -s bash lib.sh файл без shebang — как bash
  • shellcheck -f diff backup.sh готовые правки в виде diff

Обзор

shellcheck — линтер для скриптов bash и sh. Он читает текст скрипта, ничего не выполняя, и показывает места, где скрипт сломается или поведёт себя не так, как задумано. Замечание — это строка скрипта, под ней галочка ^-- на проблемном месте, код SCxxxx, уровень и объяснение:

text
student@ubuntu:~$ shellcheck /tmp/posix.sh

In /tmp/posix.sh line 2:
if [[ -f x ]]; then echo y; fi
   ^--------^ SC3010 (warning): In POSIX sh, [[ ]] is undefined.

Часто ниже идёт Did you mean: и исправленная строка. Для cp $f /tmp/ это SC2086 (info): Double quote to prevent globbing and word splitting. и предложение cp "$f" /tmp/. По коду замечания есть страница с разбором: https://www.shellcheck.net/wiki/SC2086. Коды SC1xxx — ошибки разбора текста, SC2xxx — ошибки смысла, SC3xxx — то, чего нет в POSIX sh.

Уровень Что значит
error скрипт не сработает так, как написан
warning почти наверняка ошибка
info сработает, пока не встретит пробел, пустое значение, \
style можно проще или надёжнее

Какой оболочкой проверять, shellcheck узнаёт из строки #!: в примере выше файл начинается с #!/bin/sh, и [[ ]] отмечен — в sh его нет. Код завершения: 0 — замечаний нет, 1 — есть, 2 — файл не прочитан.

Частые замечания

Код Что не так Как исправить
SC2086 $var без кавычек делится на слова "$var"
SC2164 cd без проверки — дальше работа идёт в другом каталоге cd /srv/app || exit
SC2045 цикл по выводу ls for f in "$dir"/*
SC2162 read без -r портит \ read -r line
SC2181 $? вместо проверки самой команды if ! cmd; then
SC2034 переменная не используется — часто опечатка в имени проверь имя
SC2115 rm -rf "$dir/"* при пустой dir удалит /* "${dir:?}/"*
SC2155 export x=$(cmd) прячет код cmd x=$(cmd), потом export x

Ключи

8

Основные ключи — в таблице ниже. Полный список — man shellcheck.

Ключ Что делает Пример
-f --format tty|gcc|diff|json Вид вывода; gcc — одна строка на замечание, diff — готовые правки. shellcheck -f gcc backup.sh
-S --severity error|warning|info|style Показывать замечания этого уровня и выше. shellcheck -S warning *.sh
-e --exclude SC2086,SC2034 Не показывать замечания с этими кодами. shellcheck -e SC2034 backup.sh
-s --shell bash|sh|dash|ksh Какой оболочкой проверять, если в файле нет строки shellcheck -s bash lib.sh
-x --external-sources Читать файлы, подключённые через source или точку. shellcheck -x deploy.sh
# shellcheck disable= SCxxxx Комментарий в скрипте — не проверять это в следующей команде. # shellcheck disable=SC2086
-o --enable CHECK|all Включить необязательные проверки; список — shellcheck --list-optional. shellcheck -o all backup.sh
# shellcheck shell= bash Первой строкой файла без # shellcheck shell=bash

Ничего не найдено. Попробуй запрос короче.

Рецепты

5

Проверка перед запуском или в CI

bash
shellcheck -S warning scripts/*.sh && echo "замечаний нет"

С -S warning замечания уровней info и style не печатаются и не влияют на код: 1 будет, только если остались warning или error.

Отключить замечание в одном месте

bash
# shellcheck disable=SC2086
rsync $RSYNC_OPTS src/ dst/   # опции намеренно делятся на слова

Комментарий действует на следующую команду. Пиши рядом причину: через месяц она будет не очевидна.

Отключить замечание для всего каталога

Файл .shellcheckrc в каталоге скрипта или выше:

text
disable=SC2034

Скрипт подключает другой файл

bash
shellcheck -x deploy.sh               # прочитать файлы из source
# shellcheck source=lib/common.sh     # путь для source с переменной

Без -x shellcheck пишет SC1091 … Not following и функции из подключённого файла не видит.

Посмотреть готовые правки

text
student@ubuntu:~$ shellcheck -f diff /tmp/bad.sh
--- /tmp/bad.sh
+++ /tmp/bad.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 src=$1
-cd /var/backups
-for f in $(ls $src); do
-  cp $f /tmp/
+cd /var/backups || exit
+for f in $(ls "$src"); do
+  cp "$f" /tmp/

Готовую правку дают простые замечания: кавычки, || exit. Читать diff — в diff-patch.

Неполадки

4

SC2148: Tips depend on target shell

В файле нет строки #!, и shellcheck не знает, какой оболочкой его читать. Допиши #!/bin/bash или, для подключаемого файла, первой строкой # shellcheck shell=bash.

openBinaryFile: does not exist

Файла по этому пути нет: опечатка в имени или запуск не из того каталога. shellcheck выходит с кодом 2.

Замечание не по делу

shellcheck не знает замысла: иногда разбиение на слова или переменная из другого файла нужны намеренно. Отключи замечание комментарием # shellcheck disable=SCxxxx над строкой и напиши причину.

shellcheck молчит, а скрипт падает

shellcheck проверяет только текст. Он не знает, есть ли файл, хватит ли прав, какой PATH у службы и что вернёт команда. Такие ошибки видны при запуске: проверяй коды и журнал.

Опасно

1

Применять «Did you mean» не глядя

Подсказка чинит одно замечание и не видит замысла. В примере выше for f in $(ls "$src") получил кавычки, но всё ещё перебирает вывод ls — замечание SC2045 осталось. А кавычки вокруг переменной, которая намеренно делится на несколько опций (rsync $RSYNC_OPTS), склеят их в один аргумент — команда сломается. Для набора опций заведи массив: opts=(-a --delete); rsync "${opts[@]}" src/ dst/.

Термины

1
линтер linter
Программа, которая проверяет текст кода без запуска и указывает на вероятные ошибки и опасные приёмы; для скриптов оболочки — shellcheck.

См. также

  • bash-scripting — сам скрипт: переменные, условия, циклы.
  • exit-codes — коды, которые shellcheck учит проверять.
  • test-conditions — [ ] и [[ ]], где кавычки важнее всего.
  • command-syntax — кавычки и деление строки на аргументы.
  • globbing — шаблоны вместо цикла по выводу ls.
  • diff-patch — как читать вывод -f diff.
  • bash — оболочка, для которой пишутся скрипты.
Загрузка…