カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

「シェルスクリプトを書く際に気を付けていること8箇条」の補足を書いた

前回のエントリで色々コメント頂いたので、自分の勉強のためにちょいと調べてみた。コメントありがとうございます。

とりあえず、bashオンリーのもの途中から書いてるじゃんという話し。
ひとまず、提示したソースをそのまま使ってみて、やってみた。

declare -r var1=1
echo "var1 = $var1"   # var1 = 1
(( var1++ ))          # x.sh: line 4: var1: readonly variable

これですな。これのシェバングをshとbashに変えただけ。

$ ./bash_declare.sh
var1 = 1
./bash_declare.sh: line 5: var1: readonly variable

よしよし。

$ ./sh_declare.sh
var1 = 1
./sh_declare.sh: line 5: var1: readonly variable

ほぅ。

$ file /bin/sh
/bin/sh: symbolic link to `bash'

ほぅ。。。 % man sh やりましたとさ。。。

BASH(1)
                      BASH(1)

NAME
       bash - GNU Bourne-Again SHell

手元の雪豹でも一緒だった。 今、シェバングを\#!/bin/shして、どはまりするのは古い古いサーバだけなんじゃいのか。。と思ってる。
誰かそれ違うだろってあれば、コメントお願いします。。。

って終わってはまずいので、bash,shの違いはもういいので、tcsh使ってるところあるかもしれないから、やってみた。
シェバングをtcshに変えただけ。

$ ./tcsh_declare.sh
declare: Command not found.
var1: Undefined variable.

まぁ、bash専門ですよ、しょうがない。bash勧めるのはダメでしょうか。。。
tcshでreadonly使いたい場合は以下のようにすべきでしょうな。

#!/bin/tcsh
set -r var1=1
#set var1=1
echo "var1 = $var1"   # var1 = 1
@ var1++;
echo "${var1}"

「set -r」使えばいいだけ。
ここまできたら、無理にシェルスクリプトでやる必要はないかもしれない。
気軽にできるから使いたいのでして。

可読性をあげるスタイルにこだわる

ということで、開き直ってbashの話にしますが、「スタイル」という意味でいくつか示唆を頂いたので、書いてみる。

まず、 「set -euって書くならshebangに#!/bin/bash -euって書く方が1行減りそうなんだけど何か違うの?」
これはごもっともで、一行減らしたいので、従いますです。以下のコードは全てこれで。

コマンド編

「あと、個人的にはバッククォートを使わないで$()を使うようにしてる。'と`って死ぬほど見間違えるので。bashオンリーだけど。」
「個人的にはRET=\`cmd1\`じゃなくて絶対RET=$(cmd1)を使う.もしかして/bin/kshだけの実装???」

#!/bin/bash -eu

declare -i RET=0
declare -r CURL='/usr/bin/curl'
declare -r URL='http://www.google.com'

# こっちはバッククォートでコマンド実行
RET=`"${CURL}" -tlo "${URL}"`

# こっちは$()でコマンド実行
RET=$("${CURL}" -tlo "${URL}")
echo "${RET}"

確かにバッククォートとシングルクォートは似てるからなぁ。しばらく$()使ってみて、読み易く感じるか試してみるのもありかもしれない。

この辺はmikedaさんが補足してるけど、この記事で書かれてるけど、ネストした場合は確かにねぇ。ただ、「$」をこれ以上使いたくないという悩みもありつつ。限界かぁ。

変数名編

「変数の前には_をつける派」
「変数は大域とlocalで大/小文字を使い分けるの好み。」
「環境変数は大文字、シェル変数は小文字かな、カーニンハン&パイク流。」

確かに、「環境変数」「グローバル変数」「ローカル変数」等々ありますので、区別しといたほうが本当はよいでしょうね。

僕の場合、dotifilesをいじる際に$HOMEなどの環境変数はよくシェルスクリプトで使うので、シェル内で使う変数とはほんとは区別したほうがよいのかも。その一方でそこまでそれ以外の環境変数を使う出番はそこまでないかも。

関数が必要になるほどの処理はLLに任せたいけど、そういうローカル変数に「_」を使うのはオススメですな。これは他の言語でもちょくちょく使う。

デバッグ編

「-xのデバッグ解説もあると親切に思う。」
これは確かにそうですね。mikedaさんも書いてくれてるので、是非そちらも。

$ /bin/bash -x test.sh

みたいにしてbashのオプションとしてxを指定してあげる。
実際実行するとこんな感じ。

$ /bin/bash -x sh_declare.sh

+ declare -r var1=1
+ echo 'var1 = 1'
var1 = 1
+ ((  var1++  ))
sh_declare.sh: line 5: var1: readonly variable

こうやって処理の一連の流れが見える。わりと長めの処理とか、複数の処理が入っちゃって、echo文であまりメッセージ出してないとか、ループの途中で止まってる場合なんかよいかと。

どこで止まってるのかわかるので。

終わりに

色々書くと分かりづらいですが、重要なのは、「保守しやすく」「読みやすく」「例外処理などの適切な処置がされている」このあたりなんじゃないかと思ってます。なので、それを満たす方法であれば、心がけたコードでとりあえずは充分なんじゃないかと思う。ちょっと意識するだけでも随分違うし。

こういう記事を契機に「あぁ、こういう書き方のほうがよいかもな」って思ってもらって、それおGithubなり社内なりどこかで活用して頂ければなと。

まぁ、あくまでシェルスクリプトでさせることはシンプルなものに留めるのが平和だと思うし、デバッグもしにくくなっちゃう。そんな立場で書いてはいますが、少しでも何か参考になればと思い、まとめてみた次第です!
何かあればKenichi MASUDA (@masudaK) | Twitterまで!!