Skip to content
2023-08-16

bashでの変数判定に使えるtestコマンドのオプションの挙動

bashのtestコマンドでは、変数が定義済みかどうかを判別するのに使えるオプションが3つほどあります。ただ、それらの判定結果が解せないというか、なんとなく使って見るだけだと理解できなかったので検証してみました。

検証するオプション

変数定義の判定に使えるオプションは以下の3種です。今回はこれらの挙動を検証します。

-n, -zはあくまでも文字数を判定するオプションであり、変数定義自体を評価しないことに注意が必要です。-vは変数定義自体を評価します。

※頑張って調べても-vオプションの公式情報(manやドキュメント、ソースコード等)を得られませんでした。非公式情報によれば、bashバージョン4.2以降で使えるらしいです。

解せないポイント

実際に実行して確認

確認には以下の3種の変数を使用します。これらの変数をtestコマンドの各オプションで判定することで動作を確認します。

$ VAR_FULL=hogehoge
$ VAR_EMPTY=

echoの結果は以下のとおりです。見かけ上、VAR_EMPTYVAR_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_EMPTYVAR_NODEFの結果に差が出ました。変数をechoした見た目上の結果は同じ空白でも、変数定義されているかどうかはきちんと分けられているようです。

結論

-n, -zオプションは、文字列が空白かどうかの判定に使えます。変数定義自体は絶対にするけど、文字列が入っているかどうかはわからないシェルスクリプトで際に役に立ちそうです。使用する際は、判定する変数をダブルクォートで囲うことを忘れないようしましょう。

一方、-vオプションは変数定義自体の判定に使えます。if等の条件分岐により定義されない可能性がある変数を扱うシェルスクリプトで役立ちそうです。判定したい変数には$をつけてしまわないように注意してください。