条件分岐(if / test)
条件によって処理を分けるには if 文を使います。if 条件; then ... fi の形が基本で、必要に応じて elif や else を足します。条件の判定には test コマンド、または同じ働きをする [ ... ] を使い、ファイルの有無は -f、文字列の一致は =、数値の比較は -eq や -lt などで調べます。[ の後ろと ] の前にはスペースが必要な点が初学者のつまずきどころです。
条件分岐は、状況に応じてスクリプトの動きを変えるための仕組みだ。
コマンドをただ直線的に並べただけのスクリプトは、いつも同じことしかできない。だが現実の作業は「ファイルがあるかどうか」「数が一定以上かどうか」で振る舞いを変えたいことばかりだ。
「ファイルが存在すれば処理する、無ければ作る」「入力された数が一定以上なら警告を出す」といった判断を、人手を介さずスクリプト自身に行わせられる。
bash で条件分岐を担うのが if 文だ。これを覚えると、スクリプトは単なる作業の自動化から、状況を見て振る舞いを変える小さなプログラムへと一段進化する。
🔀 if 文の形
if 文の基本形は if 条件; then 処理; fi だ。条件が成り立つ(真である)ときだけ then から fi の間の処理が実行される。終わりは if を逆さに読んだ fi で閉じる、という独特の書き方を覚えてほしい。
条件が成り立たなかったときの処理を書きたいときは else を、複数の条件を順に試したいときは elif(else if の短縮)を加える。実際には次のように書く。
if [ "$ten" -ge 80 ]; then echo "合格(優)"; elif [ "$ten" -ge 60 ]; then echo "合格"; else echo "不合格"; fi。これは点数が入った変数 ten を上から順に調べ、80以上なら「合格(優)」、そうでなく60以上なら「合格」、どれにも当てはまらなければ「不合格」を表示する。最初に成り立った枝の処理だけが実行され、残りは評価されない。
ここで一つ落とし穴がある。then を if と同じ行に続けて書くときは、その手前にセミコロン ; が必要なのだ(if [ ... ]; then)。改行して別の行に then を書くなら ; は要らない。
このセミコロンの有無もよくあるつまずきどころで、then を同じ行に書いたのに ; を忘れると構文エラーになる。
🧱 角カッコの正体
if の後ろに書く「条件」を判定しているのは、実は test というコマンドだ。そして [ ... ] という角カッコの書き方は、この test のもう一つの姿である。
意外に思うかもしれないが、[ は test という名前の別名であり、独立したコマンドなのだ。
ここから重要な注意点が導かれる。[ の直後と ] の直前には、必ずスペースを入れなければならない。[ "$a" = "b" ] は正しく、["$a"="b"] のように詰めて書くとエラーになる。
理由は、「[ という名前のコマンドに、$a や = や ] を引数として渡している」と考えれば腑に落ちる。コマンドと引数の間に空白が要るのと同じ理屈だ。
この空白の付け忘れは初学者が条件分岐でつまずく最大の原因なので、強く意識してほしい。
🔢 数値と文字列で記号が違う
条件の中で使う比較は、数値・文字列・ファイルの3系統に分かれる。
数値の比較には専用の記号を使い、-eq(等しい)、-ne(等しくない)、-lt(より小さい)、-le(以下)、-gt(より大きい)、-ge(以上)がある。それぞれ equal、not equal、less than、less or equal、greater than、greater or equal の頭文字だと考えると覚えやすくなる。
文字列の比較には =(等しい)、!=(等しくない)、-z(長さが0=空である)、-n(長さが0でない=中身がある)を使う。
ファイルの状態を調べるファイルテストには、-f(普通のファイルとして存在する)、-d(ディレクトリとして存在する)、-e(種類を問わず存在する)がある。
ここで肝心なのは、数値の比較と文字列の比較で記号が違う点だ。数が等しいかは -eq、文字列が等しいかは =、と使い分ける。
分けるのは、bash にとって 10 と 010 は文字列としては別物でも数値としては同じであり、どちらの目で比べるかを記号で指示する必要があるからだ。これを取り違えると意図しない判定になるので注意してほしい。
ファイルの有無を調べる典型例を見ておこう。file="/etc/hosts" としたうえで、if [ -f "$file" ]; then echo "$file はあります"; else echo "$file はありません"; fi と書けば、そのファイルが存在するかどうかで表示を切り替えられる。
-f はファイルの存在確認の定番で、ディレクトリかどうかを見たいなら -d、存在しさえすればよいなら -e に置き換える。
ここでも変数を "$file" とダブルクオートで囲んでいる点に注目してほしい。値が空になり得る変数を裸で書くと、条件式の形が崩れて構文エラーになることがあるためだ。前章で習った囲む癖が、ここで命綱になる。
🔗 「AかつB」の書き方
実務では「AかつB」「AまたはB」のように複数の条件を組み合わせたい場面がよくある。やり方は2通りある。
ひとつは条件式同士を && や || でつなぐ方法で、if [ -f "$file" ] && [ -r "$file" ]; then ... のように書くと、「ファイルが存在し、かつ読み取り可能なら」という意味になる(-r は読み取り権限の有無を調べるファイルテスト)。|| を使えば「どちらか一方でも成り立てば」になる。
もうひとつは [ ] の中で -a(かつ)や -o(または)を使う古い書き方だが、こちらは式が複雑になると壊れやすいため、現在は条件式を && や || でつなぐ方法が推奨される。
後述の [[ ]] を使う場合は、その内側にそのまま && や || を書けるので、if [[ -f "$file" && -r "$file" ]]; then ... とよりすっきり書ける。
🎯 if が本当に見ているもの
if 文が内部で見ているのは、実は条件の「真偽」そのものではなく、コマンドの終了ステータスという数値だ。
すべてのコマンドは終了時に0〜255の整数を返し、0が成功、0以外が失敗を意味する。直前のコマンドの終了ステータスは変数 $? で取り出せる。
test([ ])も、条件が成り立てば0を、成り立たなければ0以外を返すコマンドであり、if はその0/非0を見て分岐しているのだ。
この仕組みを知ると、if grep -q error log.txt; then ... のように、test を使わずコマンドの成否そのもので分岐させる書き方も理解できる。
なお bash には [ ] を強化した [[ ]] という書き方もあり、こちらは空変数でも式が壊れにくく、&&(かつ)や ||(または)を条件の中に直接書け、== によるパターン一致も使えるという利点がある。bash で書くと決まっている環境なら [[ ]] のほうが事故が少なく、どのシェルでも動く移植性が要るなら [ ] を選ぶ、という使い分けが実務の目安だ。
最後によくある失敗を挙げておくと、角カッコ前後の空白の入れ忘れ、数値比較に = を使ってしまう取り違え、変数のクオート漏れによる空変数での式崩れ、この3つが代表格だ。条件分岐がうまく動かないときは、まずこの3点を疑うと早く原因にたどり着ける。