タイトルがほぼすべてだが、bashの変数判定-n
の挙動のメモ
要約
変数の文字列のチェックでは、""
で囲ったほうが意図した動作を行いやすい。以下は思いつくまでの経緯。
状況
関数の中で外側で宣言されていた変数を参照しようとすると期待した通りに動かないことがあった。
下の例ではfilepath
変数に値がセットされているかどうかで関数の中で分岐を行う場合を考えている。
if文の判定では、[ -n $filepath ]
を行い、空でない値がセットされているときに真になるようにした。
filepath
は初期状態で空文字""
でこのまま関数が実行される場合もあり、そのときは、標準入力を読むようにした。
しかし、初期状態のまま関数を実行したとき、filepath
に値がセットされていないにもかかわらず、セットされているかのような振る舞いが行われたので、困惑した。
以下は、そのスクリプト例と対策をしたバージョンと原因を載せておく。
これだと期待通りの動作をしない
まずは期待通りに動かず困惑した例から。
filepath
に何かセットすれば普通に判定されるが、そのまま関数を実行するとfilepath
に何かセットされているように動く。
# これはおかしい
filepath=""
afunction() {
echo $filepath
if [ -n $filepath ]; then
# filepathが空でもここにくる。
if [ -f $filepath ]; then
# ぶっちゃけここまでくる。
echo "read from $filepath"
else
echo "$filepath is not a file"
fi
else
while read line
do
echo "read: ${line}"
done
fi
}
# そのまま実行
echo "expect to read from stdin"
afunction
妥協策
対策としては単純に-n
でなく、-z
で文字列の長さが0であることを判定するようにしたらOKだった。
# これはふつうに動く
filepath=""
afunction() {
echo $filepath
if [ -z $filepath ]; then
while read line
do
echo "read: ${line}"
done
else
if [ -f $filepath ]; then
#TODO: read
echo "read from $filepath"
else
echo "$filepath is not a file"
fi
fi
}
# そのまま実行
echo "expect to read from stdin"
afunction
原因
単純に-n
オペレータによる判定について何か勘違いしているというのが、一番考えられる。
もう少しテストしてみた結果、ダブルクォーテーションで囲っておけば、上の動かない例のものでも問題なく動くことが分かった。
# ncheck.sh
emptyvar=""
avar="hello"
if [ -n $emptyvar ]; then
echo "pass emptyvar"
fi
if [ -n $avar ]; then
echo "pass avar"
fi
if [ -n "${emptyvar}" ]; then
echo "pass emptyvar with double quotes"
fi
if [ -n "${avar}" ]; then
echo "pass avar with double quotes"
fi
この結果として下の出力が得られる:
$ ./ncheck.sh
pass emptyvar
pass avar
pass avar with double quotes
[ -n "${filpath}" ]
が期待通りの動作に必要な記述だったようだ。-n
も-z
も文字列に対する演算子なのでよく考えてみれば当然だった。
文字列変数のチェックならダブルクォートで囲む。
以上です。