bashのtestコマンドでは、変数が定義済みかどうかを判別するのに使えるオプションが3つほどあります。ただ、それらの判定結果が解せないというか、なんとなく使って見るだけだと理解できなかったので検証してみました。
検証するオプション
変数定義の判定に使えるオプションは以下の3種です。今回はこれらの挙動を検証します。
-n
: 文字数が1文字以上なら0
(True)、0文字なら1
(Flase)-z
: 文字数が0文字なら0
(True)、1文字以上なら1
(Flase)(-n
オプションとは逆の結果が出る)-v
: 変数が定義されていれば0
(True)、定義されていなければ1
(False)
-n
, -z
はあくまでも文字数を判定するオプションであり、変数定義自体を評価しないことに注意が必要です。-v
は変数定義自体を評価します。
※頑張って調べても-v
オプションの公式情報(manやドキュメント、ソースコード等)を得られませんでした。非公式情報によれば、bashバージョン4.2以降で使えるらしいです。
解せないポイント
- 変数定義しているのに判定に引っかかったり引っかからなかったりする
- ダブルクォートで囲うかどうかで結果が変わる
実際に実行して確認
確認には以下の3種の変数を使用します。これらの変数をtestコマンドの各オプションで判定することで動作を確認します。
VAR_FULL
: 変数定義され、文字列も入っているVAR_EMPTY
: 変数定義はするが、文字列は入ってっていないVAR_NODEF
: 変数定義しないし、もちろん文字列も入っていない
$ VAR_FULL=hogehoge
$ VAR_EMPTY=
echoの結果は以下のとおりです。見かけ上、VAR_EMPTY
とVAR_NODEF
は同じですが、上記の通り変数定義を行ったかどうかに差があります。
[fujio@general ~]$ echo $VAR_FULL
hogehoge
[fujio@general ~]$ echo $VAR_EMPTY
[fujio@general ~]$ echo $VAR_NODEF
[fujio@general ~]$
● -n オプション
VAR_FULL
はダブルクォートの有無で結果は変わりませんでした。どちらの結果も、変数が展開されたhogehoge
という1文字以上の文字列が判定に使われるためです。
[fujio@general ~]$ test -n $VAR_FULL; echo $?
0
[fujio@general ~]$ test -n "$VAR_FULL"; echo $?
0
VAR_EMPTY
はダブルクォートの有無で結果が変わってしまいました。
ダブルクォートで囲わなかった場合はtest -n
という引数指定のない不適切なコマンドが実行されます。testコマンドは(試した感じだと)構文が適切でない場合はTrueを返すようですので、ダブルクォートで囲わなかった場合の結果もTrueになったものと思われます。ダブルクォートで囲った場合、test -n ""
というコマンドが実行されます。""
は文字数0なので、結果がFalseなのは適切です。
[fujio@general ~]$ test -n $VAR_EMPTY; echo $?
0
[fujio@general ~]$ test -n "$VAR_EMPTY"; echo $?
1
VAR_NODEF
の結果もVAR_EMPTY
と同じといえます。
[fujio@general ~]$ test -n $VAR_NODEF; echo $?
0
[fujio@general ~]$ test -n "$VAR_NODEF"; echo $?
1
● -z オプション
-n
オプションと比較すると、ダブルクォートで囲った場合の結果は変わりませんでした。
一方、ダブルクォートで囲った場合は0
1
が逆の結果になりました。
それぞれの実行結果の詳細は割愛します。検証したときの実行結果だけ記載します。
[fujio@general ~]$ test -z $VAR_FULL; echo $?
1
[fujio@general ~]$ test -z "$VAR_FULL"; echo $?
1
[fujio@general ~]$ test -z $VAR_EMPTY; echo $?
0
[fujio@general ~]$ test -z "$VAR_EMPTY"; echo $?
0
[fujio@general ~]$ test -z $VAR_NODEF; echo $?
0
[fujio@general ~]$ test -z "$VAR_NODEF"; echo $?
0
● -v オプション
VAR_FULL
は、ダブルクォートで囲っても囲わなくても「変数未定義」という判定になりました。これは、変数VAR_FULL
が展開された結果hogehoge
が出てきましたので、変数hogehoge
が存在するかどうかの判定が行われたためです。そんな変数は当然定義していないので判定結果はFalseになります。
[fujio@general ~]$ test -v $VAR_FULL; echo $?
1
[fujio@general ~]$ test -v "$VAR_FULL"; echo $?
1
VAR_EMPTY
はダブルクォートで囲わないばあいはとTrueになりました。前述の通り、変数が展開された結果不適切な構文になったため、Trueが返されたのだと思います。ダブルクォートで囲った場合になぜこの結果になったのか確証は持てませんが、""
という変数は定義されていないためにFalse担ったのだと思います。
[fujio@general ~]$ test -v $VAR_EMPTY; echo $?
0
[fujio@general ~]$ test -v "$VAR_EMPTY"; echo $?
1
VAR_NODEF
の結果はVAR_EMPTY
の結果と同様です。
[fujio@general ~]$ test -v $VAR_NODEF; echo $?
0
[fujio@general ~]$ test -v "$VAR_NODEF"; echo $?
1
ここまで来て、-v
オプションについては使い方が間違っている可能性に気づきました。-v
オプションでは変数に$
をつけてはいけないようです。よく考えてみれば当たり前でした。bashはコマンドラインの実行より先に変数を展開しますので、$
を使って変数を指定するとコマンドライン実行前に変数が展開されてしまいます。結果、判定したかった変数はコマンドラインから消滅してしまいます。変数がなくなってしまうので、当然に変数が定義されているかの判定を行うことはできなくなります。
-v
オプションで変数に$
をつけずに判定すると以下の結果となりました。
[fujio@general ~]$ test -v VAR_FULL; echo $?
0
[fujio@general ~]$ test -v VAR_EMPTY; echo $?
0
[fujio@general ~]$ test -v VAR_NODEF; echo $?
1
変数定義されているかどうかを正しく判定できました。ここでついに、VAR_EMPTY
とVAR_NODEF
の結果に差が出ました。変数をechoした見た目上の結果は同じ空白でも、変数定義されているかどうかはきちんと分けられているようです。
結論
-n
, -z
オプションは、文字列が空白かどうかの判定に使えます。変数定義自体は絶対にするけど、文字列が入っているかどうかはわからないシェルスクリプトで際に役に立ちそうです。使用する際は、判定する変数をダブルクォートで囲うことを忘れないようしましょう。
一方、-v
オプションは変数定義自体の判定に使えます。if等の条件分岐により定義されない可能性がある変数を扱うシェルスクリプトで役立ちそうです。判定したい変数には$
をつけてしまわないように注意してください。