UNIX/Linuxの部屋 testコマンドの使い方

TOP UNIX/Linuxの部屋 UNIX/Linuxコマンド一覧 用語集 新版 由来/読み方辞書 環境変数マニュアル Cシェル変数 システム設定ファイル システムコール・ライブラリ ネットワークプログラミングの基礎知識 クラウドサービス徹底比較・徹底解説




コマンド test 条件式評価プログラム このエントリーをはてなブックマークに追加

最終更新


UNIX/Linux の test コマンドは、主にシェルスクリプトで使われる、条件判定のためのコマンドである。



test コマンドとは何か
sh のシェルスクリプトで条件式を書く場合、
if [ "$var" = 'hoge' ]; then
...
fi
などと記述する。この [ というのは実はコマンドで、またの名を test という。

test コマンドとは、引数で渡された条件式が成り立つなら (真なら) 0、成り立たないなら (偽なら) 1 を返すコマンドなのである。

実際に試してみよう。
% test 1 = 1 ; echo $?
0
% test 1 = 2 ; echo $?
1
1 は 1 と等しいので真、1 は 2 と等しくないので偽となる。基本的に test と [ は同じなのだが、一点だけ異なるのは [ で起動すると、引数の最後に ] を要求することである。つまり、以下の 2 つのコマンドは等価である。
% test 1 = 1 ; echo $?
0
% [ 1 = 1 ]; echo $?
0

これで
if [ "$var" = 'hoge' ]; then
...
fi
の真実が見えただろうか。この if 文は一見、
if [ 式 ]; then
...
fi
と見えるが、そうではなく、
if コマンド; then
...
fi
なのである。つまり if は、直後にある文字列をコマンドとみなして実行し、その戻り値が 0 ならば then 〜 fi を実行するシェルの構文なのである。であるから、
if ls; then
echo "ls の実行に成功しました"
else
echo "ls の実行に失敗しました"
fi
は、sh スクリプトとしては正しい (が、こういうわけのわからん書き方をしてはならない)。

test コマンドの条件式
test コマンドが解釈できる主な条件式は以下の通り。
s1 = s2		文字列 s1 と s2 が同じであれば真
s1 != s2	文字列 s1 と s2 が同一でなければ真
s1 < s2		文字列 s1 が文字列 s2 に対し、文字の ASCII 順において前に来るなら真
s1 > s2		文字列 s1 が文字列 s2 に対し、文字の ASCII 順において後に来るなら真
n1 -eq n2	整数 n1 と n2 が等しければ真
n1 -ne n2	整数 n1 と n2 が等しくなければ真
n1 -gt n2	整数 n1 が n2 がより大きければ真
n1 -ge n2	整数 n1 が n2 より大きいか等しければ真
n1 -lt n2	整数 n1 が n2 より小さければ真
n1 -le n2	整数 n1 が n2 より小さいか等しければ真

ファイルテスト演算子
主なファイルテスト演算子は以下の通り。
-f foo		ファイル foo が存在するなら真。foo がディレクトリやシンボリックリンクの場合は真にはならない。
-d foo		ディレクトリ foo が存在するなら真
-L foo		シンボリックリンク foo が存在するなら真
-e foo		ファイル foo が存在するなら真。foo がディレクトリやシンボリックリンクでもよい。
-r foo		ファイル foo が存在し、読み込み可能なら真
-w foo		ファイル foo が存在し、書き込み可能なら真
-x foo		ファイル foo が存在し、実行可能なら真
file1 -nt file2	ファイル file1 が file2 より新しければ真 (nt は newer than)
file1 -ot file2	ファイル file1 が file2 より古ければ真 (ot は older than)

and・or・グルーピング
上記の式は -a や -o で繋げることで AND/OR を表現できる、また、カッコで囲むことでグルーピングが可能。
if [ "$foo" = 'x' -o '(' "$bar" = 'y' -a "$baz" = 'z' ')' ]; then
...
fi

汎用的なシェルスクリプト
sh でも csh でも、本当に汎用的なプログラムを書くのは難しい。以下のスクリプトは、標準入力から 1行読み込み、それが "q" であれば "QUIT" という文字列を出力するだけのプログラムである。
#!/bin/sh
read input
if [ $input = 'q' ]; then
echo QUIT
fi
しかしこのスクリプトはいくつか問題がある。まず、入力待ちのときにいきなり Enter キーを押した場合、
[: =: unexpected operator
とエラーになってしまう。これは $input が空文字になり、
if [ $input = 'q' ]; then

if [ = 'q' ]; then
と等価とみなされたためである。test コマンドは、いきなり = が出現したためエラーとみなしたわけだ。これを回避するためには
if [ "$input" = 'q' ]; then
と $input をダブルクォートで囲めばよい。しかしまだ落とし穴はある。たとえば "!" を入力すると、
[: =: unexpected operator
とまたしてもエラーになってしまう。これは "$input" が "!" であるため、
if [ ! = 'q' ]; then
として扱われたからだ。test コマンドは否定を表す ! の後にいきなり = が出現したため、エラーとしてしまう。これを回避するためには、
if [ " $input" = ' q' ]; then
if [ "X$input" = 'Xq' ]; then
などと、比較する両辺の先頭に空白や 'X' などの文字を入れればよい。

test コマンドの小ネタ
test コマンドで有名な笑い話として、
「/bin/ の下に [ っていう変なコマンドがあったので、消してしまった」
というものがある。また、test.c というテストプログラムを書いて、
% cc -o test test.c
% test
としたが、カレントディレクトリの test でなく /bin/test が実行されて悩んでしまう、というケースもある。もしこの例で /bin/test でなく、カレントディレクトリの test が実行されてしまうとしたら、それは PATH の設定を見直した方がよい。

test と [ の関係
FreeBSD で /bin を ls してみると、
% ls -li /bin/[ /bin/test
143183 -r-xr-xr-x 2 root wheel 54496 Nov 21 13:46 /bin/[
143183 -r-xr-xr-x 2 root wheel 54496 Nov 21 13:46 /bin/test
となる。i-node が一致している (ハードリンクされている) ので、/bin/[ と /bin/test は同じコマンドであることがわかる。

一方で、Solaris のように /bin/[ が存在しない OS もあるし、Linux (CentOS 5.8 で確認) では、下記のようにハードリンクされておらず、バイナリサイズも異なる (しかしながらソースは同じである)。
% ls -li /usr/bin/[ /usr/bin/test
1836213 -rwxr-xr-x 1 root root 37000 5月 11 17:59 2016 /usr/bin/[
1835846 -rwxr-xr-x 1 root root 33808 5月 11 17:59 2016 /usr/bin/test

さらに一部の sh や bash では、test と [ は内部コマンドになっている。
$ type test
test is a shell builtin
$ type [
[ is a shell builtin

初期の UNIX では test と [ は /bin にのみ存在していたが、その後プロセス生成のオーバーヘッドを抑えるため、シェルの内部コマンドとして実装したのであろう。
>> Linuxオンラインマニュアル(man) Linux test(1)
>> Solaris10オンラインマニュアル(man) Solaris10 test(1)
>> FreeBSDオンラインマニュアル(man) FreeBSD test(1)