対象
- bashでスクリプトを書こうとしている人
- bashでスクリプトを書くことをやめさせたいので、説得のためのネタを探してる人
はじめに
暗黙的とは、特に指定していないのに自動で行われる処理を示してます。
型の自動変換や環境変数による挙動の差異など、知らないで実装すると後々トラブルになる可能性があるため、スクリプトを書く際の注意を自分なりにまとめておきます。
暗黙的な動作の説明
ループなどに指定するデータの区切り文字は IFS
環境変数が効いている
以下はどのような動作になるかわかるでしょうか?
$ dat="a b c d"
$ for i in ${dat}
do
echo $i
done
上記は IFS
環境変数によって挙動が異なります。
IFSには区切り文字を定義することができるため、IFS=","
という前提があると、上記プログラムの出力結果は a b c d
になります。
ある関数でIFSを上書き設定している記述があり、そのせいで後続のfor文が想定外の動作を起こすという素敵な事故がありました。
|
を使うとサブプロセスになる
$ command1 | command2
上記のコマンドは以下のように解釈されます。
$ ( command1 | command2 )
bashでは ()
がつくとサブシェル扱いになるため、サブシェル内で変数を定義しても親シェルに引き継ぐ事ができません。
このため、whileなどは記述によって動作が変わります。
$ wc -l test.txt
4 test.txt
$ cnt=0
$ cat test.txt | while read line
do
cnt=`expr ${cnt} + 1`
done
$ echo $cnt
0
$ cnt=0
$ while read line
do
cnt=`expr ${cnt} + 1`
done < test.txt
$ echo $cnt
4
こちらは基本的に実装時にわかりますし、影響範囲も少ないので迷惑度は少ないと思います。
引数の展開
grepを使う時などの注意で、shellはまずワイルドカードを展開してからコマンドを解釈します。
正規表現には *
などのshellの展開に使う記述もあるので、これが原因で意図しない動作に繋がることがあります。
例えば以下のテキストファイルからfoo*txtに該当する行を取る場合。
foo.txt
foobar.txt
barfoo.txt
bar.txt
以下は悪い例で、ワイルドカードが展開されてからgrepしてしまうため foobar.txt
が抽出できません。
$ ls -1
foo.txt
$ grep foo.* foo.txt
foo.txt
barfoo.txt
展開したくないなら '
囲みにするなどして対応する
$ grep 'foo.*' foo.txt
foo.txt
foobar.txt
barfoo.txt
気をつけろよ!で済めばいいんですが、レビュー漏れすると本番にリリースした後、しばらくしてから「何もしてない」のに不具合が出るということにも・・・。
最後に
滅多なことでは問題にならないと思うけど、マーフィーの法則とかもあわせて考えるとやっぱ怖い。