关于 ShellCheck
ShellCheck 分析 Shell 脚本,发现常见错误、不可移植性问题和代码问题。支持批量检查多个脚本,提供具体的问题诊断和改进建议。
主要功能
- 01检测 Shell 脚本中的语法错误和逻辑问题
- 02识别跨平台兼容性问题
- 03批量分析多个脚本文件
适用场景
- 01在 CI/CD 流程中自动验证 Shell 脚本质量
- 02调试复杂 Shell 脚本的问题
- 03改进基础设施自动化脚本的可靠性
安装
选择你的系统或包管理器,复制命令到终端运行。
安装完成后运行shellcheck --version确认安装成功。
命令示例
常用命令按使用顺序排列。
详细介绍
ShellCheck - Shell 脚本静态分析工具
ShellCheck 是一款 GPLv3 工具,可为 bash/sh Shell 脚本提供警告和建议:

ShellCheck 的目标是
-
指出并解释典型的新手语法问题,这些问题会导致 Shell 给出晦涩难懂的错误消息。
-
指出并解释典型的中级语义问题,这些问题会导致 Shell 的行为古怪且违背直觉。
-
指出细微的注意事项、边界情况和陷阱,这些问题可能导致高级用户原本正常运行的脚本在未来遇到某些情况时失效。
请参阅糟糕代码示例集,了解 ShellCheck 可以帮助你发现哪些问题!
目录
如何使用
ShellCheck 有多种使用方式!
在网页上
将 Shell 脚本粘贴到 https://www.shellcheck.net,即可立即获得反馈。
ShellCheck.net 始终与最新的 git 提交保持同步,是试用 ShellCheck 最简单的方式。快告诉你的朋友!
在终端中
在终端中运行 shellcheck yourscript,即可立即查看输出,如上所示。
在编辑器中
你可以直接在多种编辑器中查看 ShellCheck 的建议。
。
。
-
Sublime,可通过 SublimeLinter 使用。
-
Pulsar Edit(原 Atom),可通过 linter-shellcheck-pulsar 使用。
-
VSCode,可通过 vscode-shellcheck 使用。
-
大多数其他编辑器,可通过 GCC 错误兼容格式使用。
在构建或测试套件中
ShellCheck 主要面向交互式使用,但也很容易集成到构建或测试套件中。
它会按规范使用退出代码,因此你只需将 shellcheck 命令添加到流程中即可。
例如,在 Makefile 中:
check-scripts:
# Fail if any of these files have warnings
shellcheck myscripts/*.sh
或在 Travis CI 的 .travis.yml 文件中:
script:
# Fail if any of these files have warnings
- shellcheck myscripts/*.sh
已预装 ShellCheck 并可直接使用的服务和平台:
- Travis CI
- Codacy
- Code Climate
- Code Factor
- Codety,通过 Codety Scanner
- CircleCI,通过 ShellCheck Orb
- Github(仅限 Linux)
- Trunk Code Quality(通用代码检查工具;允许你明确指定 shellcheck 的安装版本),通过 shellcheck 插件
- CodeRabbit
包括 GitLab 在内的大多数其他服务,都允许你自行安装 ShellCheck,可通过系统的软件包管理器(参见安装), 也可下载并解压二进制发行版。
无论如何,手动安装指定版本的 ShellCheck 都是个好主意。这样可以避免发布包含新警告的新版本时,导致构建意外失败。
如需自定义筛选或报告,ShellCheck 可以输出简单的 JSON、兼容 CheckStyle 的 XML、 兼容 GCC 的警告,以及便于阅读的文本(可带或不带 ANSI 颜色)。更多文档请参阅 Integration wiki 页面。
安装
在本地安装 ShellCheck,最简单的方法是使用包管理器。
在使用 Cabal 的系统上(安装到 ~/.cabal/bin):
cabal update
cabal install ShellCheck
在使用 Stack 的系统上(安装到 ~/.local/bin):
stack update
stack install ShellCheck
在基于 Debian 的发行版上:
sudo apt install shellcheck
在基于 Arch Linux 的发行版上:
pacman -S shellcheck
或者从 AUR 获取无需依赖的 shellcheck-bin。
在基于 Gentoo 的发行版上:
emerge --ask shellcheck
在基于 EPEL 的发行版上:
sudo yum -y install epel-release
sudo yum install ShellCheck
在基于 Fedora 的发行版上:
dnf install ShellCheck
在 FreeBSD 上:
pkg install hs-ShellCheck
在 macOS(OS X)上使用 Homebrew:
brew install shellcheck
或使用 MacPorts:
sudo port install shellcheck
在 OpenBSD 上:
pkg_add shellcheck
在 openSUSE 上
zypper in ShellCheck
或使用 OneClickInstall - https://software.opensuse.org/package/ShellCheck
在 Solus 上:
eopkg install shellcheck
在 Windows 上(通过 chocolatey):
C:\> choco install shellcheck
或在 Windows 上(通过 winget):
C:\> winget install --id koalaman.shellcheck
或在 Windows 上(通过 scoop):
C:\> scoop install shellcheck
从 conda-forge 安装:
conda install -c conda-forge shellcheck
从 Snap Store 安装:
snap install --channel=edge shellcheck
从 Docker Hub 获取:
docker run --rm -v "$PWD:/mnt" koalaman/shellcheck:stable myscript
# Or :v0.4.7 for that version, or :latest for daily builds
或者,如果你想使用更大的、基于 Alpine Linux 的镜像进行扩展,可以使用 koalaman/shellcheck-alpine。它的工作方式与普通 Alpine 镜像完全相同,但已预装 shellcheck。
使用 mise:
mise use shellcheck
使用 nix 包管理器:
nix-env -iA nixpkgs.shellcheck
使用 Flox 包管理器
flox install shellcheck
或者,你可以在此下载最新版本的预编译二进制文件:
- Linux,x86_64(静态链接)
- Linux,armv6hf,即 Raspberry Pi(静态链接)
- Linux,aarch64,又称 ARM64(静态链接)
- Linux,riscv64(静态链接)
- macOS,aarch64
- macOS,x86_64
- Windows,x86
或者查看 GitHub Releases 获取其他版本 (包括每日 Git 构建的最新元版本)。
发行版软件包已附带 man 手册页。如果你是从源代码构建的,可以通过以下方式安装:
pandoc -s -f markdown-smart -t man shellcheck.1.md -o shellcheck.1
sudo mv shellcheck.1 /usr/share/man/man1
pre-commit
要通过 pre-commit 运行 ShellCheck,请将钩子添加到 .pre-commit-config.yaml:
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.11.0
hooks:
- id: shellcheck
# args: ["--severity=warning"] # Optionally only show errors and warnings
Travis CI
Travis CI 现在默认集成了 ShellCheck,因此你无需手动安装。
如果你仍想安装,以便在方便时升级,或确保使用的是最新版本, 请按照以下步骤安装二进制版本。
安装预编译二进制文件
预编译二进制文件以 tar.xz 文件的形式提供。要解压,请确保已安装
xz。
在 Debian/Ubuntu/Mint 上,你可以运行 apt install xz-utils。
在 Redhat/Fedora/CentOS 上,运行 yum -y install xz。
简单的安装程序可能如下所示:
scversion="stable" # or "v0.4.7", or "latest"
wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${scversion?}/shellcheck-${scversion?}.linux.x86_64.tar.xz" | tar -xJv
cp "shellcheck-${scversion}/shellcheck" /usr/bin/
shellcheck --version
从源代码编译
本节介绍如何从源代码目录构建 ShellCheck。ShellCheck 使用 Haskell 编写,编译需要 2GB RAM。
安装 Cabal
ShellCheck 使用 Cabal 构建和打包。请使用系统的软件包管理器(例如 apt-get、brew、emerge、yum 或 zypper)安装软件包 cabal-install。
在 macOS(OS X)上,你可以通过 brew 快速安装 Cabal,只需几分钟;如果尝试从源代码编译,则需要 30 多分钟。
$ brew install cabal-install
在 MacPorts 上,该软件包名为 hs-cabal-install;原生 Windows 用户则应从 https://www.haskell.org/platform/ 安装最新版本的 Haskell platform。
确认已安装 cabal,并使用以下命令更新其依赖项列表:
$ cabal update
编译 ShellCheck
使用 git clone 克隆此仓库,然后进入 ShellCheck 源代码目录进行构建和安装:
$ cabal install
这将编译 ShellCheck,并将其安装到 ~/.cabal/bin 目录。
将此目录添加到 PATH(对于 bash,请将以下内容添加到 ~/.bashrc):
export PATH="$HOME/.cabal/bin:$PATH"
注销并重新登录,然后确认 PATH 设置正确:
$ which shellcheck
~/.cabal/bin/shellcheck
在原生 Windows 上,PATH 应已设置好,但系统可能使用旧版代码页。在 cmd.exe、powershell.exe 和 Powershell ISE 中,请确保使用 TrueType 字体,而不是 Raster 字体,并使用 chcp 将活动代码页设为 UTF-8(65001):
chcp 65001
在 Powershell ISE 中,你可能还需要更新输出编码:
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
运行测试
运行单元测试套件:
$ cabal test
错误代码示例
那么 ShellCheck 会检查哪些问题?以下是检测到的问题的不完整列表。
引号
ShellCheck 可以识别多种引号使用错误:
echo $1 # Unquoted variables
find . -name *.ogg # Unquoted find/grep patterns
rm "~/my file.txt" # Quoted tilde expansion
v='--verbose="true"'; cmd $v # Literal quotes in variables
for f in "*.ogg" # Incorrectly quoted 'for' loops
touch $@ # Unquoted $@
echo 'Don't forget to restart!' # Singlequote closed by apostrophe
echo 'Don\'t try this at home' # Attempting to escape ' in ''
echo 'Path is $PATH' # Variables in single quotes
trap "echo Took ${SECONDS}s" 0 # Prematurely expanded trap
unset var[i] # Array index treated as glob
条件语句
ShellCheck 可以识别多种错误的测试语句。
[[ n != 0 ]] # Constant test expressions
[[ -e *.mpg ]] # Existence checks of globs
[[ $foo==0 ]] # Always true due to missing spaces
[[ -n "$foo " ]] # Always true due to literals
[[ $foo =~ "fo+" ]] # Quoted regex in =~
[ foo =~ re ] # Unsupported [ ] operators
[ $1 -eq "shellcheck" ] # Numerical comparison of strings
[ $n && $m ] # && in [ .. ]
[ grep -q foo file ] # Command without $(..)
[[ "$$file" == *.jpg ]] # Comparisons that can't succeed
(( 1 -lt 2 )) # Using test operators in ((..))
[ x ] & [ y ] | [ z ] # Accidental backgrounding and piping
经常误用的命令
ShellCheck 可以识别命令使用不当的情况:
grep '*foo*' file # Globs in regex contexts
find . -exec foo {} && bar {} \; # Prematurely terminated find -exec
sudo echo 'Var=42' > /etc/profile # Redirecting sudo
time --format=%s sleep 10 # Passing time(1) flags to time builtin
while read h; do ssh "$h" uptime # Commands eating while loop input
alias archive='mv $1 /backup' # Defining aliases with arguments
tr -cd '[a-zA-Z0-9]' # [] around ranges in tr
exec foo; echo "Done!" # Misused 'exec'
find -name \*.bak -o -name \*~ -delete # Implicit precedence in find
# find . -exec foo > bar \; # Redirections in find
f() { whoami; }; sudo f # External use of internal functions
常见的初学者错误
ShellCheck 可以识别许多常见的初学者语法错误:
var = 42 # Spaces around = in assignments
$foo=42 # $ in assignments
for $var in *; do ... # $ in for loop variables
var$n="Hello" # Wrong indirect assignment
echo ${var$n} # Wrong indirect reference
var=(1, 2, 3) # Comma separated arrays
array=( [index] = value ) # Incorrect index initialization
echo $var[14] # Missing {} in array references
echo "Argument 10 is $10" # Positional parameter misreference
if $(myfunction); then ..; fi # Wrapping commands in $()
else if othercondition; then .. # Using 'else if'
f; f() { echo "hello world; } # Using function before definition
[ false ] # 'false' being true
if ( -f file ) # Using (..) instead of test
风格
ShellCheck 可以提出改进风格的建议:
[[ -z $(find /tmp | grep mpg) ]] # Use grep -q instead
a >> log; b >> log; c >> log # Use a redirection block instead
echo "The time is `date`" # Use $() instead
cd dir; process *; cd ..; # Use subshells instead
echo $[1+2] # Use standard $((..)) instead of old $[]
echo $(($RANDOM % 6)) # Don't use $ on variables in $((..))
echo "$(date)" # Useless use of echo
cat file | grep foo # Useless use of cat
数据和类型错误
ShellCheck 可以识别与数据和类型相关的问题:
args="$@" # Assigning arrays to strings
files=(foo bar); echo "$files" # Referencing arrays as strings
declare -A arr=(foo bar) # Associative arrays without index
printf "%s\n" "Arguments: $@." # Concatenating strings and arrays
[[ $# > 2 ]] # Comparing numbers as strings
var=World; echo "Hello " var # Unused lowercase variables
echo "Hello $name" # Unassigned lowercase variables
cmd | read bar; echo $bar # Assignments in subshells
cat foo | cp bar # Piping to commands that don't read
printf '%s: %s\n' foo # Mismatches in printf argument count
eval "${array[@]}" # Lost word boundaries in array eval
for i in "${x[@]}"; do ${x[$i]} # Using array value as key
健壮性
ShellCheck 可以提出增强脚本健壮性的建议:
rm -rf "$STEAMROOT/"* # Catastrophic rm
touch ./-l; ls * # Globs that could become options
find . -exec sh -c 'a && b {}' \; # Find -exec shell injection
printf "Hello $name" # Variables in printf format
for f in $(ls *.txt); do # Iterating over ls output
export MYVAR=$(cmd) # Masked exit codes
case $version in 2.*) :;; 2.6.*) # Shadowed case branches
可移植性
如果使用了 shebang 不支持的功能,ShellCheck 会发出警告。例如,如果将 shebang 设为 #!/bin/sh,ShellCheck 会针对可移植性问题发出警告,类似于 checkbashisms:
echo {1..$n} # Works in ksh, but not bash/dash/sh
echo {1..10} # Works in ksh and bash, but not dash/sh
echo -n 42 # Works in ksh, bash and dash, undefined in sh
expr match str regex # Unportable alias for `expr str : regex`
trap 'exit 42' sigint # Unportable signal spec
cmd &> file # Unportable redirection operator
read foo < /dev/tcp/host/22 # Unportable intercepted files
foo-bar() { ..; } # Undefined/unsupported function name
[ $UID = 0 ] # Variable undefined in dash/sh
local var=value # local is undefined in sh
time sleep 1 | sleep 5 # Undefined uses of 'time'
其他
ShellCheck 还能识别各种其他问题:
PS1='\e[0;32m\$\e[0m ' # PS1 colors not in \[..\]
PATH="$PATH:~/bin" # Literal tilde in $PATH
rm “file” # Unicode quotes
echo "Hello world" # Carriage return / DOS line endings
echo hello \ # Trailing spaces after \
var=42 echo $var # Expansion of inlined environment
!# bin/bash -x -e # Common shebang errors
echo $((n/180*100)) # Unnecessary loss of precision
ls *[:digit:].txt # Bad character class globs
sed 's/foo/bar/' file > file # Redirecting to input
var2=$var2 # Variable assigned to itself
[ x$var = xval ] # Antiquated x-comparisons
ls() { ls -l "$@"; } # Infinitely recursive wrapper
alias ls='ls -l'; ls foo # Alias used before it takes effect
for x; do for x; do # Nested loop uses same variable
while getopts "a" f; do case $f in "b") # Unhandled getopts flags
用户评价
起初你会觉得“shellcheck 真棒”,但随后就会想“wtf,我们怎么还在用 bash”
Alexander Tarasikov,
来自 Twitter
忽略问题
可以通过环境变量、命令行、单独设置或在文件中全局设置来忽略问题:
https://github.com/koalaman/shellcheck/wiki/Ignore
报告错误
如有错误或功能建议,请使用 GitHub 问题跟踪器:
https://github.com/koalaman/shellcheck/issues
参与贡献
请通过 GitHub 拉取请求提交代码或文档补丁!请查看 ShellCheck Wiki 上的 DevGuide。
贡献内容必须采用 GNU GPLv3 许可。
贡献者保留版权。
版权
ShellCheck 采用 GNU 通用公共许可证 v3 授权。此许可证的副本包含在文件 LICENSE 中。
版权所有 2012-2019,Vidar 'koala_man' Holen 及贡献者。
祝你愉快地使用 ShellCheck!