rcmdnk's blog
Last update

20141126_shellcheckcli_200_200

前から気になってた ShellCheck を使ってシェルスクリプトや.bashrc等直してみました。

Sponsored Links

ShellCheck

ShellCheck はシェルスクリプトの文法をチェックしてくれるオンラインサービスです。

ShellCheckのページに行って 試したいスクリプトを書き込むと文法をチェックして エラーや注意を表示してくれます。

指摘してくれるものとしては、明らかな文法違反な物は勿論、 実際にスクリプトを回して毎回エラーになるものでなくても、 変数が空の場合に""で囲ってないとエラーになる場合なども チェックしてくれるので思わぬ事故も防いでくれます。

shellcheckweb

コマンドライン版

ShellCheckのソースコードはGitHubで公開されていて、 さらにコマンドライン版のshellcheckを使うことも出来ます。

MacならHomebrewで一発:

$ brew install shellcheck

LinuxでもDebian系なんかだとapt-getでインストールが出来ます。

$ apt-get install shellcheck

もしくはCabal を使ってインストールする方法がGitHubのREADMEで紹介されています。

$ cabal update
$ cabal install shellcheck

コマンドライン版の使い方は、

$ shellcheck <file>

でOK。

オプションとしては:

  • -e CODE1,CODE2.., --exclude=CODE1,CODE2..: SC2045など、shellcheckが返すエラーコードを指定して、そのエラーや警告を無視する。
  • -f FORMAT, --format=FORMAt: 出力フォーマットの指定。
    • tty: 通常のテキストアウトプット(デフォルト)。
    • gcc: gccライクなアウトプット。
    • checkstyle: XML形式。
    • json: Json形式。
  • -s SHELL,--shell=SHELL: sh, bash, ksh, zshを与えてシェルの指定(デフォルトはbash)。
  • -V: バージョン情報。
  • -h: ヘルプ。

取り敢えず インストール後、.bashrcshell check .bashrcで チェックしてみると

shellcheckcli

こんな感じでエラーやらが出てきます。

gcc形式にすると

$ shellcheck -f gcc .bashrc
...
.bashrc:48:20: error: Don't quote rhs of =~, it'll match literally rather than as a regex. [SC2076]
.bashrc:59:13: warning: Quote this to prevent word splitting. [SC2046]
.bashrc:98:9: note: Double quote to prevent globbing and word splitting. [SC2086]
.bashrc:100:11: note: Double quote to prevent globbing and word splitting. [SC2086]
...

こんな感じ。 どんなエラーが多いかと言うことで調べてみると

$ shellcheck -f gcc .bashrc | cut -d' ' -f2-|sort|uniq -c|sort -n
   1 error: Double quote array expansions, otherwise they're like $* and break on spaces. [SC2068]
   1 note: $/${} is unnecessary on arithmetic variables. [SC2004]
   1 note: Shebang (#!) missing. Assuming Bash. [SC2148]
   1 note: Use find instead of ls to better handle non-alphanumeric filenames. [SC2012]
   1 note: echo won't expand escape sequences. Consider printf. [SC2028]
   1 warning: Assigning an array to a string! Assign as array, or use * instead of @ to concatenate. [SC2124]
   1 warning: This expands when defined, not when used. Consider escaping. [SC2139]
   1 warning: Use "$@" (with quotes) to prevent whitespace problems. [SC2048]
   2 error: Aliases can't use positional parameters. Use a function. [SC2142]
   2 note: Expressions don't expand in single quotes, use double quotes for that. [SC2016]
   2 warning: Don't use variables in the printf format string. Use printf "..%s.." "$foo". [SC2059]
   2 warning: Quote this to prevent word splitting. [SC2046]
   6 note: Use $(..) instead of deprecated `..` [SC2006]
  12 error: Don't quote rhs of =~, it'll match literally rather than as a regex. [SC2076]
  39 note: Double quote to prevent globbing and word splitting. [SC2086]
$

こんな出てました。

一番多いのは

In a.sh line 29:
  if [ -r $arg ]; then
          ^-- SC2086: Double quote to prevent globbing and word splitting.

と言った変数をダブルクォートで囲みなさい、とう忠告。 空白を含んでいたりする時に2つに別れて認識されてしまったりするのを回避しないと行けません。 単にechoしてるだけのところとかも指摘されるので、エラーではなくnoteになってますが、 一応気をつけてるつもりですが結構空白があり得る所にも残ってました。。。

次に多かったのはエラーで

In .bashrc line 48:
if [[ "$OSTYPE" =~ "darwin" ]];then
                   ^-- SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.

と言った、=~を使った場面での右辺をクォートしてしまっている所。 この手の式は大概左辺に右辺の文字列を含んでいたら、的な意味合いで使っていますが、 右辺には正規表現を使うことが出来るので、正規表現を使った時に 思ってるのと違う様になるよ、ということ。 .bashrcの中で使ってるのは正規表現を使ったものは 無いのでどちらでも一緒ですが、正しく使うためにクォートを外しました。

他には``で囲って式の値を取るのではなく、 $()を使いなさい、といったものや、 printfの引数の中では直接変数を使わずに%s等を使ってフォーマット形式にしなさいとか $(())の中では変数に$とか要らないとか、 引数全部を文字列として一括で渡す時に$@としてでなく$*で渡せとか 1 。。。

他のエラーとかも説明に従って直していけば簡単に直していけると思います。

色々とチェックをしてくれますが、 About ShellCheck にはチェックされる項目の一部が載っています。

追記: 2014/12/02

また、GitHubのレポジトリの Wiki には各エラーなどの細かい説明や直し方も載っているので非常に参考になります。

追記ここまで

In a.sh line 257:
alias hischeck="history|awk '{print \$4}'|sort|uniq -c|sort -n"
      ^-- SC2142: Aliases can't use positional parameters. Use a function.

と言ったエラーもでましたが、これはawkの中で使ってる$4aliasにしてるコマンド自身に渡す引数と勘違いされてます。

これを今後チェックする時に避けてもらうためには-e SC2141shellcheck に渡してしまっても良いのですが、これだと他で実際このエラーが有るときも無視してしまいます。

この部分だけ無視するには、この行の直前に

# shellcheck disable=SC2142
alias hischeck="history|awk '{print \$4}'|sort|uniq -c|sort -n"

の様に、shellcheck disable=~というコメント行を加えます。 複数無視したい場合はdisableを複数書くか、disable=SC2142,SC2143 の様にコンマでつなげても行ける様です。

日本語を含んだファイル

日本語を含んだファイルなんかをチェックしようとすると

shellcheck: user error (Text.Regex.Posix.String died: (ReturnCode 17,"illegal byte sequence"))

と言ったエラーが出てチェックできないので、 どうしてもやりたい場合には一時的にその部分だけコメントアウトするなり 日本語以外の部分だけコピーしてチェックするなりする必要があります。

Vimでは

Vimでは syntasticプラグインが入っていれば、 shellcheckをインストールした段階で自動的にシェルスクリプトの編集時には Syntasticshellcheckを使うようになってより強力にチェック出来るので、 vim+Syntasticを使ってる場合には、 shellcheckをコマンドラインで使わなくても取り敢えず入れておくと便利です。

shellcheckvim

Sponsored Links
  1. val="$@"
    

    val="$*"
    

    は同じ物ですが、

    val=("$@")
    

    val=("$*")
    

    では、前者は配列としとして$1,$2…が順に入りますが、 後者は$1 $2...と全てが連結した文字列が配列の0番目に入ります。

    なので普通にval="$@"とやる分には別に問題は無いと思いますが、 その辺きちんと理解しときなさい、と言う意味でwarningを出してる感じだと思います。

Sponsored Links

« Jekyll(Octopress)プラグインを作る際にmarkupの最後の空白に気をつける ブログをモバイルフレンドリーに »