ShellScript
Bash
Linux
Ubuntu

shell scriptの基本を学ぶ testコマンドの条件一覧

testコマンド

testコマンドの基本については, 前回の記事: shell scriptの基本を学ぶ 縁の下の力持ち testコマンドとifの使い方を参照してください.

今回は, testコマンドで使われる様々な条件を紹介していこうと思います.

いきなりイレギュラーから

何も入力しない

testコマンドを, 条件や引数を明示せず, 単体で実行すると, falseになります.

shell scriptではなく, そのままbash上でコマンドを叩いてみましょう.

$ test
$ echo $?
1

exit statusが1になっていて, falseになっています.


ここで注意点ですが, bashは, 普通のプログラミング言語とは異なり, 0がtrueで, 0以外は, falseです.

完全に逆になっているので, しっかり意識しておかないとあれ?と思うことがあると思います.


!

true, falseを否定する効果を持ちます.

プログラミング言語でも馴染みがあると思います.

上の条件を明示しないと, falseになることを使って, !をつけて, trueにしてみましょう.

bash上で確認してみましょう.

$ test !
$ echo $?
0

trueになってますね.

条件がついている場合も見てみます.

$ test 1 -lt 2
$ echo $?
0
$ test ! 1 -lt 2
$ echo $?
1

-ltはあとで紹介しますが, 左辺の整数が右辺より整数より小さいときにtrueになります.

!をつけたものとつけないもので, trueかfalseが違っていることが見て取れると思います.

-a -o

条件をかつまたはを使って,複合的に組み合わせるときに使います.

-aは, andのaなので, 右辺と左辺の条件がともにtrueのときに, trueになります.

-oは, orのoなので, 右辺と左辺の条件のどちらかがtrueのときに, trueになります.

-ltを使って, 試してみましょう.

test
#!/bin/bash
test 1 -lt 2 -a 1 -lt 2
echo "The result of \"1 -lt 2 -a 1 -lt 2\" is $?"
test 1 -lt 2 -a 2 -lt 1
echo "The result of \"1 -lt 2 -a 2 -lt 1\" is $?"
test 2 -lt 1 -a 1 -lt 2
echo "The result of \"2 -lt 1 -a 1 -lt 2\" is $?"
test 2 -lt 1 -a 2 -lt 1
echo "The result of \"2 -lt 1 -a 2 -lt 1\" is $?"

実行すると,

The result of "1 -lt 2 -a 1 -lt 2" is 0
The result of "1 -lt 2 -a 2 -lt 1" is 1
The result of "2 -lt 1 -a 1 -lt 2" is 1 
The result of "2 -lt 1 -a 2 -lt 1" is 1

-oのほうも各自試してみるといいでしょう.

まず文字列に対する条件を紹介します.

STRING

-n STRING

-n STRINGは, STRINGの長さが, 1以上である場合, true.

0である場合は, false.

自分は, the length is Nonzeroとnと覚えています.

確認するためのshell scriptを書いてみましょう.

test2
#!/bin/bash
while :
do
    read X
    if [ -n "$X" ]; then
        echo 'The length of $X is not nonzero'
    else
        echo 'The length of $X is nonzero'
        echo "THE PROGRAMMING ENDS"
        break
    fi
done

-nのあとの"$X"は, "が必須です.

裸の$Xのままだと, $Xが空のとき, [ -n $X ]がtrueになってしまうからです.

確かめてみてください(もしかして環境依存かもしれません).

-nは引数があるかどうかなどの判定をするときに, 使われることが多いです.

また, -nを省略しても同じ意味合いになります.

-z STRING

これは, -n(nonzeroのn)と逆で, 文字列の長さが0のとき, trueになります.

zeroの頭文字のzとおぼえましょう.

サンプルを動かして確認してみましょう.

test3
#!/bin/bash
while :
do
    read input
    if [ -z $input ]; then
        echo "you didn't type anything"
    elif [ $input = "bye" ]; then
        echo "see you"
        break
    else 
        echo "you typed $input"
    fi
done

入力待ちの状態で, Enterを押すと,

-z $inputがtrueになって, "you didn't input anything"が表示されることが確認できると思います.

次に整数に関する条件を紹介します.

INTEGER

-eq

-eqは, 整数用の比較演算子で, 等しい場合に, trueになります.

equalのeqと覚えましょう.

サンプルを作ってみましょう.

test4
#!/bin/bash
while :
do
    magicNumber=7
    echo "Guess the magic number between 1 and 9"
    read int
    if [ $int -eq $magicNumber ]; then
        echo "You hit the magic number: $magicNumber"
        break
    else 
        echo "Nope, this is NOT the magic number"
    fi
done

magic numberの7を当てるまで, 終わらないscriptです.

-ne

-eqと逆で, -neは, 2つの整数が異なるときに, trueになります.

test5
#!/bin/bash
rand=$(shuf -i 1-100 -n 1)
rand2=$(shuf -i 1-100 -n 1)
while :
do
    echo "$rand + $rand2 ="
    read usrAns
    ans=$(expr $rand + $rand)
    if [ $usrAns -ne ans ]; then
        echo "Wrong"
    else
        break
    fi
done

これは, 2つのランダムの数字を生成して, その足し算の問題を出します.


間違えた場合, Wrongを出力します.

他に知らない構文などが出てきているので, 説明します.

$()は, `(backtick, バッククォート)で囲むと同じ意味です.

囲まれたコマンドの出力がそのまま置き換わります.

shuf -i 1-100 -n 1は, 1から100の整数を一つランダムで, 生成します.


-lt -le -gt -ge

整数を比較してくれる演算子です.

左辺が, 右辺と比較して,

-lt -le -gt -ge
小さい 小さいか等しい 大きい 大きいか等しい

のとき, trueになります.

覚え方は, less thanのlt, less than or equal toのle
greater thanのgt, greater than or equal toのgeと覚えましょう.

サンプルは省略します.

次は, ファイルに関する条件です.

FILE

FILEは, そのファイルのパスを指定すればokです.

-nt -ot

FILE1 -nt FILE2は, FILE1の更新日が, FILE2の更新日が新しい場合, true.

逆に, FILE1 -ot FILE2は, FILE1の更新日が, FILE2の更新日が古い場合, true.

-ntは, newer thanの略, -otは, older thanの略です.

実際にshell scriptを作って, 試してみましょう.

test6
#!/bin/bash
touch a
touch b
if [ a -nt b ]; then
    echo "a is newer than b"
else 
    echo "b is newer than a"
fi

touchコマンドで, abというからファイルを作成します(存在していれば, 更新日を変更してくれます).

bのほうが, あとに作られているので, 新しいです.

よって, [ a -nt b ]は, falseになります.

-otは, 自分で試してみましょう.

-ef

man testの説明をそのまま説明するならば,

FILE1 -ef FILE2は, FILE1FILE2が, 同じデバイスを持っていて, かつ同じinode(ファイルの実態を表すための目印)を持っている場合に, trueになります.

日本語でおけって感じな人もいると思うので説明します.

まずLinuxのファイルシステムには, inodeというものがあります.

これは, ファイルの実態がおいてあるアドレスのようなものです.

実際に, ファイル名のinodeを参照して, ファイルの実態を取ってきています.

ディレクトリやファイル自体が, ファイルの実態を持っているわけではなく, ファイル名は, inodeをもったただの参照です.

inodeを確認するには, ls-iオプションを付けると, inodeを見ることができます.

さて, inodeが同じということは, 同じ実体を参照しているということになります.

テキストの内容が同じでも, 同じinodeを持つことはありません.

cpでコピーしても, 違うinodeを持ちます.

じゃあどういうときにinodeがおなじになるのか.

ln-sオプションを付けずハードリンクを作ったときです(他にもあるのかもしれません).

じゃあ確認してみましょう.

$ touch a
$ cp a b
$ ln a c
$ ls -i a b c 
 28710447624513918 a  124974889659546761 b   28710447624513918 c 

acは, ハードリンクなので, 同じinodeになりました.

cpで作ったbは, aとは全く違うinodeを持っています.

このinodeを比較して等しい場合は, trueになるのが -efです.

同じデバイスというのは, デバイスごとに, inodeが区切られていて,

inodeが同じでも, デバイスが異なっている場合は, 違う実ファイルを参照しています.

-efは, eqaul fileの略(?)と覚えましょう.

-d

ここらかは単号演算子(一つの引数しか取らない)になります.

-d FILE は, FILEが存在し, かつ, ディレクトリだった場合, trueになります.

予め, touch sample_filemkdir sample_dirを叩いて, 空のファイルと空のディレクトリを作っておきましょう.

test7
#!/bin/bash
if [ -d sample_file ]; then
    echo "sample_file is a directory"
else 
    echo "sample_file is not a directory"
fi

if [ -d sample_dir ]
    echo "sample_dir is a directory"
else 
    echo "sample_dir is not a direcotry"
fi
$ ./test7
sample_file is not a directory
sample_dir is a directory

-e

-e FILEは, FILEが存在する場合は, trueを返します.

existのeと覚えましょう.

-r

-r FILEは, FILEが存在し, 読む権限がある場合は, trueを返します.

readのrと覚えましょう.]

-w

-w FILEは, FILEが存在し, 書き込み権限がある場合は, trueを返します.

writeのwで覚えましょう.

-x

-x FILEは, FILEが存在し, 実行権限がある場合は, trueを返します.

-h -L

-h FILEは, FILEがシンボリックリンクの場合は, trueを返します.

Lでも同じ意味合いになります.

symbolic LinkのLで覚えましょう.

後半は適当になりましたが, このくらいわかれば, いいと思います.

他にもあるので, man testを眺めて, shell scriptを書いてみると, 面白いと思います.