admin avatar

shellcheck 幫助新手的你寫出更好的shell腳本

🕚 by admin

簡介

shellcheck 是一款實用的shell腳本靜態檢查工具。

首先,可以幫助你提前發現並修復簡單的語法錯誤,節約時間。每次都需要運行才發現寫錯了一個小地方,確實非常浪費時間。 其次,可以針對你當前不夠完善不夠健壯的寫法,提供建議,幫助你提前繞開一些坑,避免等問題真的發生了才去調試處理。

在其介紹中,目標是針對所有用戶的,從初學者到高手,都用得上

  • 指出並澄清典型的初學者的語法問題,那通常會shell提供神秘的錯誤消息。
  • 指出並澄清典型的中級的語義問題,這些問題會導致shell出現奇怪且反直覺的行為。
  • 指出可能導致高級用戶的腳本中,可能在未來某種情況下失敗的陷阱。

如何使用

在網頁上使用

非常簡單,在網頁https://www.shellcheck.net上,貼入你的腳本,運行檢查即可

在命令行中使用

下載後,在命令行中調用shellcheck yourscript 即可

集成到編輯器中(推薦)

推薦將shellcheck直接集成到日常編輯器中,這樣就可以直接在編輯器中查看ShellCheck建議,以最快速度發現並修復問題。

  • Vim 通過ALE, Neomake 或Syntastic 進行集成
  • Emacs 通過Flycheck 或Flymake 集成
  • Sublime 通過SublimeLinter.
  • Atom 通過Linter.
  • VSCode 通過vscode-shellcheck.

安裝方式

在大多數發行版的包管理中,已經有shellcheck了,如在基於debian的機器上

Bash:
1
apt-get install shellcheck

其他系統的具體安裝方式,可以查閱shellcheck 的github首頁介紹

當然,也可以選擇自行從源碼安裝。

問題列表

那麼shellcheck具體會檢查一些什麼問題呢,以下給出一個不完整的問題檢查列表。 可以看下,你是否都能意識到這樣的寫法時有錯誤或隱患的。 如果發現有自己不知道的或自己容易錯漏的,那麼也許你也應該花點時間,裝上shellcheck。

引號問題

Bash:
1
2
3
4
5
6
7
8
9
10
echo $1                           # Unquoted variables  #变量未加引号
find . -name *.ogg                # Unquoted find/grep patterns #find/grep 的匹配模式未加引号
rm "~/my file.txt"                # Quoted tilde expansion #引号中的波浪符扩展
v='--verbose="true"'; cmd $v      # Literal quotes in variables # 变量中的字面引号
for f in "*.ogg"                  # Incorrectly quoted 'for' loops # 错误的for循环
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 #过早扩展陷阱

條件判斷

ShellCheck 可以識別大多數不正確的條件判斷語句

Bash:
1
2
3
4
5
6
7
8
9
10
11
[[ 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 ((..)) #在((..))中使用比较

常見的對命令的錯誤使用

ShellCheck 可以識別對一些命令的錯誤使用

Bash:
1
2
3
4
5
6
7
8
9
10
11
grep '*foo*' file                 # Globs in regex contexts  #在grep的正则表达式中前后使用通配符
find . -exec foo {} && bar {} \;  # Prematurely terminated find -exec  # 使find -exec 过早结束
sudo echo 'Var=42' > /etc/profile # Redirecting sudo # 重定向sudo
time --format=%s sleep 10         # Passing time(1) flags to time builtin # 将time(1)的标志传递给内建的time
while read h; do ssh "$h" uptime  # Commands eating while loop input  # 一个获取输入的while循环中,使用同样会获取输入的命令
alias archive='mv $1 /backup'     # Defining aliases with arguments # 定义使用参数的alias
tr -cd '[a-zA-Z0-9]'              # [] around ranges in tr # 在tr的参数范围外使用[]
exec foo; echo "Done!"            # Misused 'exec'  # 错误地使用exec
find -name \*.bak -o -name \*~ -delete  # Implicit precedence in find  # 在find中的隐式优先级
# find . -exec foo > bar \;       # Redirections in find  #find中的重定向
f() { whoami; }; sudo f           # External use of internal functions #在外部使用内部函数

初學者的常見錯誤

ShellCheck 識別很多初學者的語法錯誤

Bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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'  #使用else if
f; f() { echo "hello world; }     # Using function before definition 在函数定义之前使用函数
[ false ]                         # 'false' being true # 此处false为true
if ( -f file )                    # Using (..) instead of test #使用()取代测试条件

風格

ShellCheck 可以提出一些風格改進建議

Bash:
1
2
3
4
5
6
7
8
[[ -z $(find /tmp | grep mpg) ]]  # Use grep -q instead  #改成使用grep -q
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   #改成使用subshell
echo $[1+2]                       # Use standard $((..)) instead of old $[]  #改成使用标准的$((..))
echo $(($RANDOM % 6))             # Don't use $ on variables in $((..)) #在$((..))中不要使用$
echo "$(date)"                    # Useless use of echo # 没必要的echo
cat file | grep foo               # Useless use of cat #没必要的cat

數據和拼寫錯誤

ShellCheck 可以識別一些數據和拼寫錯誤

Bash:
1
2
3
4
5
6
7
8
9
10
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 # 在subshells中进行赋值
cat foo | cp bar                  # Piping to commands that don't read # 通过管道传递数据给一个不会做读取的程序
printf '%s: %s\n' foo             # Mismatches in printf argument count # pirintf参数数量不匹配

魯棒性

ShellCheck可以做出一些增強腳本魯棒性的建議

Bash:
1
2
3
4
5
6
7
rm -rf "$STEAMROOT/"*            # Catastrophic rm # 可能导致灾难性后果的rm
touch ./-l; ls *                 # Globs that could become options # 使用了可能变成选项的通配符
find . -exec sh -c 'a && b {}' \; # Find -exec shell injection # Find -exec shell注入
printf "Hello $name"             # Variables in printf format # 在printf的格式化参数中使用变量
for f in $(ls *.txt); do         # Iterating over ls output # 在ls的输出上进行迭代
export MYVAR=$(cmd)              # Masked exit codes # 使退出码模糊
case $version in 2.*) :;; 2.6.*) # Shadowed case branches # 隐蔽的case分支

可移植性

ShellCheck 警告你使用了shebang 不支持的特性.。 比如,當你設置shebang 為 #!/bin/sh 是, ShellCheck 對類似 checkbashisms 的可移植性問題發出警告。

Bash:
1
2
3
4
5
6
7
8
9
10
echo {1..$n}                     # Works in ksh, but not bash/dash/sh    #在 ksh 中可用,在 bash/dash/sh 中不可用
echo {1..10}                     # Works in ksh and bash, but not dash/sh #在 ksh 中可用,在 bash/dash/sh 中不可用
echo -n 42                       # Works in ksh, bash and dash, undefined in sh #在 ksh/bash/dash 中可用,在 sh 中不可用
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 # dash/sh 中未定义的变量
local var=value                  # local is undefined in sh # sh 中未定义local
time sleep 1 | sleep 5           # Undefined uses of 'time' # 使用了time未定义的用法

其他雜七雜八的問題

ShellCheck 可以識別到一些其他問題

Bash:
1
2
3
4
5
6
7
8
9
10
11
PS1='\e[0;32m\$\e[0m '            # PS1 colors not in \[..\]  # PS1 的颜色不在\[..\] 中
PATH="$PATH:~/bin"                # Literal tilde in $PATH # $PATH中的波浪号
rm “file”                         # Unicode quotes #Unicode 引号
echo "Hello world"                # Carriage return / DOS line endings # 传输返回DOS行结束符/
echo hello \                      # Trailing spaces after \   # \后面的行尾空格
var=42 echo $var                  # Expansion of inlined environment # 展开内联环境变量
#!/bin/bash -x -e                 # Common shebang errors # shebang  命令错误
echo $((n/180*100))               # Unnecessary loss of precision # 不必要的精度丢失
ls *[:digit:].txt                 # Bad character class globs # 不好的通配符
sed 's/foo/bar/' file > file      # Redirecting to input # 重定向到输入
while getopts "a" f; do case $f in "b") # Unhandled getopts flags # 未处理的getopts标志

總結

以上就是shellcheck的介紹了,主要來自其github 的readme ,源碼在github https://github.comf/koalaman/shellcheck

簡單實用,只要配置好了,就可以持續為你提供幫助。而且這個是建議性的,可以自己根據實際情況決定是否採納。即用即棄的臨時腳本,那兼容性等就不用太care。長期使用的,就還是完善一下比較穩妥。

via https://www.cnblogs.com/zqb-all/p/10054594.html

💘 相关文章

写一条评论