Bashシェルスクリプトで条件分岐を書くときによく使われるのが test 系の条件式です。
testコマンドの書き方
代表的な書き方として次の3種類があります。
- test 条件
- [ 条件 ]
- [[ 条件 ]]
このうち、testと [ ] はほぼ同じ意味を持ち、[ ] はtestコマンドの別表記です。[ はコマンドとして存在しますが、 ] は終端記号となり、コマンドとして存在しません。
# [はコマンドとして存在するのに対して、
# which [
/usr/bin/[
# ]はコマンドとして存在しない
# which ]
/usr/bin/which: no ]なお、次のようにtestコマンドを使うより、
s=""
if test -z "$s" ; then
echo "文字列の長さは0です"
else
echo "文字列の長さは1以上です"
fi実務では[ ]、もしくは[[ ]]を使う方がコードが読みやすく一般的です。
s=""
if [[ -z "$s" ]]; then
echo "文字列の長さは0です"
else
echo "文字列の長さは1以上です"
fi[ ]と[[ ]]の違い
[ ]と[[ ]]では以下のような違いがあります。
| 特性 | [ ] | [[ ]] |
|---|---|---|
| 単語分割 | 起きる | 起きない |
| ワイルドカードパス名展開(glob展開) | 起きる | 起きない |
| 未定義変数 | エラーになりやすい | 安全 |
| 論理演算 | -a / -o | && / || |
| パターンマッチ、正規表現 | – | 強力(==、!=、=~) |
| POSIX(UNIX系OSの標準規格) | 対応 | 非対応(bash等) |
一つづつ違いを見ていきます。
単語分割
スペースなどで単語が分割されているケースです。
変数sに単語を分割代入して動作を確認します。
[ ]の場合:
s="apple orange"
if [ $s = "apple orange" ]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# エラーとなり、正しく判定できない
./test1.sh: line 36: [: too many arguments
違いますこの場合、[ ]内の変数sをダブルクォーテーションで囲み、単語分割されないようにします。
s="apple orange"
if [ "$s" = "apple orange" ]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
同じです次に[[ ]]を確認します。
[[ ]]の場合:
s="apple orange"
if [[ $s = "apple orange" ]]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
同じですダブルクォーテーションで囲まなくても単語分割が起きないのが分かります。
ワイルドカードパス名展開(glob展開)
[ ]でワイルドカード(*)を使うとパス名展開されます。
変数sに存在しないファイル「test1.txt」を指定します。
s="test1.txt"
if [ $s = test1* ]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
違います次に存在するファイル「test1.sh」を変数sに代入します。
s="test1.sh"
if [ $s = test1* ]; then
echo "同じです"
else
echo "違います"
fi「test1*」でパス名展開され、「test1.sh」と一致します。
実行結果:
# ./test1.sh
同じです次に[[ ]]を確認します。
[[ ]]の場合*はパス名展開ではなく、パターンマッチ(globパターン)として使用されます。
存在しないファイル「test1.txt」を変数sに代入します。
s="test1.txt"
if [[ $s = test1* ]]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
同じです[[ ]]の場合はパス名として展開されるのではなく、「test1*」が文字列としてパターンマッチされるため「test1.txt」と一致します。
globパターンの詳細については「パターンマッチ、正規表現」の章に記載します。
未定義変数
[ ]で未定義変数を扱うとエラーになります。
unset s
if [ $s = "test1*" ]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
./test1.sh: line 35: [: =: unary operator expected
違いますこのエラーは左辺の未定義変数sが存在しない([ = “test1*” ])と判定されるからです。
[[ ]]では未定義変数はエラーになりません。
unset s
if [[ $s = "test1*" ]]; then
echo "同じです"
else
echo "違います"
fi実行結果:
# ./test1.sh
違いますこれは[[ ]]が未定義変数を空文字([[ “” = “test1*” ]])として判定するためです。
論理演算
論理演算子とは、複数の条件を組み合わせるための演算子のことです。
次の 3 つを使います。
- AND(かつ)
- OR(または)
- NOT(否定)
以下は[ ]の論理演算子です。
- AND演算子:-a
- OR演算子:-o
- NOT演算子:!
AND演算子の使用例です。OR演算子も使い方は同じです。
s="test1"
t="test2"
if [ $s = "test1" -a $t = "test2" ]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
trueです以下は[[ ]]の論理演算子です。
- AND演算子:&&
- OR演算子:||
- NOT演算子:!
AND演算子の使用例です。OR演算子も使い方は同じです。
s="test1"
t="test2"
if [[ $s = "test1" && $t = "test2" ]]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
trueです否定(NOT演算子)する場合は!になります。
s="test1"
t="test2"
if [ ! $s = "XXX" -a $t = "test2" ]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
trueです「$s = “XXX”」が否定されたことが分かります。
次に[[ ]]で試します。
s="test1"
t="test2"
if [[ ! $s = "XXX" && $t = "test2" ]]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
trueです同じく「$s = “XXX”」が否定されたことが分かります。
それでは式全体($s = “XXX” && $t = “test2″)を否定するにはどうすればよいでしょうか?
[[ ]]の場合は()で囲みます。
s="test1"
t="test2"
if [[ ! ( $s = "test1" && $t = "test2" ) ]]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
falseです[ ]の場合、()はバックスラッシュでエスケープする必要があります。
s="test1"
t="test2"
if [ ! \( $s = "test1" -a $t = "test2" \) ]; then
echo "trueです"
else
echo "falseです"
fi実行結果:
# ./test1.sh
falseです論理演算子を使用する場合は、可読性が高くシンプルに記載できる[[ ]]がお勧めです
パターンマッチ
パターンマッチを使いたいなら[[ ]]一択です。
| 種類 | 特徴 |
|---|---|
| [ ] | glob 展開(パス名展開)が起きるためパターンマッチ不可 |
| [[ ]] | glob パターンによるパターンマッチが可能。正規表現も使える |
先ずは、glob展開とglobパターンについて説明します。
glob 展開
glob パターンを、条件に一致する “実在するファイル名の一覧に置き換える処理” のことです(パス名展開とも呼ばれます)。
例:ディレクトリに a.txt と b.txt がある場合
*.txt → a.txt b.txtglob展開については前述していますので、詳細はそちらをご参照ください。
glob パターン
globパターンは、ワイルドカード(* や ? など)を使って探したいファイルの特徴を表現したものです。
*.txt→ 「.txt で終わるファイル」a?c→ 「a + 任意の1文字 + c」[0-9]*→ 「数字で始まる任意のファイル」
glob パターンは通常ファイル名の指定に使われますが、[[ ]] の中ではglob展開しないので、文字列のパターンマッチに使えます。つまり、ワイルドカードを使ったパターン記法です。
[[ ]] 構文の = / == は、右辺をクォートしなければパターンに一致するかどうかを判定します。
[[ $str == "*.txt" ]]➔ 完全一致(「*.txt」という文字列そのものか判定)[[ $str == *.txt ]]➔ パターンマッチ(.txtで終わるか判定)
以下は拡張子を判定する例です。
パターンマッチ(右辺をクォートしない場合):
filename="backup_20260630.tar.gz"
if [[ $filename == *.tar.gz ]]; then
echo "これは圧縮アーカイブファイルです。"
else
echo "圧縮アーカイブファイルではありません。"
fi実行結果:
# ./test1.sh
これは圧縮アーカイブファイルです。「backup_20260630.tar.gz」は存在しないので、glob展開しないことが分かります。
右辺をクォートした場合:
filename="backup_20260630.tar.gz"
if [[ $filename == "*.tar.gz" ]]; then
echo "これは圧縮アーカイブファイルです。"
else
echo "圧縮アーカイブファイルではありません。"
fi実行結果:
# ./test1.sh
圧縮アーカイブファイルではありません。[[ ]] の正規表現マッチ(=~)
[[ ]] の =~ は 正規表現によるマッチングを行います。 globパターンよりもはるかに強力です。
現場でよく使う「入力値が数字だけか」「IPアドレスの形式か」といったチェックが行えます。
num="1234567890"
if [[ $num =~ ^[0-9]+$ ]]; then
echo "入力されたのは正しい数値です: $num"
else
echo "エラー: 数値以外の文字が含まれています。"
fi実行結果:
# ./test1.sh
入力されたのは正しい数値です: 1234567890こちらもglobパターンと同様に、右側の正規表現パターンはクォートで囲みません。
拡張正規表現
[[ ]] の =~ は 拡張正規表現を使えます。
特徴:
+や?をそのまま使える(基本正規表現では”\+“や “\?“のようにバックスラッシュが必要)|で OR が書ける()でグループ化できる[0-9]や[A-Za-z]などの文字クラスが使える
例:
s="user100access.log"
if [[ $s =~ ^user?[0-9]+(access|error)\.log$ ]]; then
echo "正しいログ名です。: $s"
else
echo "エラー: ログ名に誤りがあります。"
fi実行結果:
# ./test1.sh
正しいログ名です。: user100access.logキャプチャも可能
キャプチャとは正規表現の () で囲んだ部分を「抜き出す」仕組みのことです。
たとえば:
item42という文字列から 数字の部分だけ を取り出したいとします。
正規表現ではこう書きます:
item([0-9]+)この ( ) の部分が キャプチャ(グループ)です。
[[ ]] で =~ を使うと、 マッチした内容が自動的に BASH_REMATCH という配列に入ります。
| 配列 | 内容 |
|---|---|
BASH_REMATCH[0] | 正規表現にマッチした全体 |
BASH_REMATCH[1] | 最初の ( ) の中身 |
BASH_REMATCH[2] | 2番目の ( ) の中身 |
| … | 以下同様 |
実行例です。
s="item-fruits-42"
if [[ $s =~ item-([a-z]+)-([0-9]+) ]]; then
echo "0: ${BASH_REMATCH[0]}"
echo "1: ${BASH_REMATCH[1]}"
echo "2: ${BASH_REMATCH[2]}"
fi実行結果:
# ./test1.sh
0: item-fruits-42
1: fruits
2: 42glob パターンと正規表現の違い(まとめ)
| 種類 | 演算子 | 例 | 説明 |
|---|---|---|---|
| glob パターン | = / == | *.txt | ファイル名マッチと同じ簡易パターン |
| 拡張正規表現(ERE) | =~ | ^a.*b$ | より強力で複雑な判定が可能 |
testコマンドオプション
testコマンドには多くのオプションがあり、大きく次の4種類に分類できます。
- 文字列
- 数値比較
- ファイル判定
- 論理演算
よく使うものについて記載します。
1.文字列オプション
| オプション | 意味 |
|---|---|
| -z “$s” | 文字列の長さが 0(空文字) |
| -n “$s” | 文字列の長さが 0 でない |
例:文字列長の確認
s=""
if [ -z "$s" ]; then
echo "文字列の長さは0です"
else
echo "文字列の長さは1以上です"
fi結果:
文字列の長さは0です2. 数値比較オプション
数値の比較には、文字列比較とは別の専用オプションを使います。= や != を数値に対して使わないよう注意してください。
| オプション | 意味 |
"$a" -eq "$b" | 数値が等しい (equal) |
"$a" -ne "$b" | 数値が等しくない (not equal) |
"$a" -gt "$b" | 数値がより大きい (greater than) |
"$a" -ge "$b" | 数値が以上 (greater or equal) |
"$a" -lt "$b" | 数値がより小さい (less than) |
"$a" -le "$b" | 数値が以下 (less or equal) |
例:数値の判定
num=10
if [ "$num" -gt 5 ]; then
echo "5より大きいです"
fi実行結果:
# ./test1.sh
5より大きいです3. ファイル判定オプション
シェルスクリプトで頻繁に使用される、ファイルやディレクトリの状態を確認するためのオプションです。
| オプション | 意味 |
-e "$file" | ファイルが存在する (exist) |
-f "$file" | 通常のファイルである |
-d "$file" | ディレクトリである |
-r "$file" | 読み取り権限がある |
-w "$file" | 書き込み権限がある |
-x "$file" | 実行権限がある |
例:ディレクトリの存在チェック
dir="./logs"
if [ -d "$dir" ]; then
echo "$dir はディレクトリです"
else
echo "$dir は存在しないか、ディレクトリではありません"
fi実行結果:
# ./test1.sh
./logs はディレクトリです4. 論理演算
複数の条件を組み合わせたい場合に使用します。
| オプション | 意味 |
! expr | 条件の否定 (NOT) |
expr1 -a expr2 | 条件の論理積 (AND) |
expr1 -o expr2 | 条件の論理和 (OR) |
例:AND条件
file="data.txt"
if [ -f "$file" -a -r "$file" ]; then
echo "ファイルが存在し、かつ読み取り可能です"
fi実行結果:
# ./test1.sh
ファイルが存在し、かつ読み取り可能です


コメント