🐧 Linux 総合学習プラットフォーム
シェルスクリプト ・ 中級

条件分岐(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 を同じ行に書いたのに ; を忘れると構文エラーになります。

条件を判定する test と [ ]

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、文字列が等しいかは =、と使い分けます。これを取り違えると意図しない判定になるので注意してください。

ファイルの有無を調べる典型例を見ておきましょう。file="/etc/hosts" としたうえで、if [ -f "$file" ]; then echo "$file はあります"; else echo "$file はありません"; fi と書けば、そのファイルが存在するかどうかで表示を切り替えられます。-f はファイルの存在確認の定番で、ディレクトリかどうかを見たいなら -d、存在しさえすればよいなら -e に置き換えます。ここでも変数を "$file" とダブルクオートで囲んでいる点に注目してください。値が空になり得る変数を裸で書くと、条件式の形が崩れて構文エラーになることがあるためです。

複数の条件を組み合わせる

実務では「AかつB」「AまたはB」のように複数の条件を組み合わせたい場面がよくあります。やり方は2通りあります。ひとつは条件式同士を && や || でつなぐ方法で、if [ -f "$file" ] && [ -r "$file" ]; then ... のように書くと、「ファイルが存在し、かつ読み取り可能なら」という意味になります(-r は読み取り権限の有無を調べるファイルテスト)。|| を使えば「どちらか一方でも成り立てば」になります。もうひとつは [ ] の中で -a(かつ)や -o(または)を使う古い書き方ですが、こちらは式が複雑になると壊れやすいため、現在は条件式を && や || でつなぐ方法が推奨されます。後述の [[ ]] を使う場合は、その内側にそのまま && や || を書けるので、if [[ -f "$file" && -r "$file" ]]; then ... とよりすっきり書けます。

終了ステータスと [[ ]]、よくある失敗

if 文が内部で見ているのは、実は条件の「真偽」そのものではなく、コマンドの終了ステータスという数値です。すべてのコマンドは終了時に0〜255の整数を返し、0が成功、0以外が失敗を意味します。直前のコマンドの終了ステータスは変数 $? で取り出せます。test([ ])も、条件が成り立てば0を、成り立たなければ0以外を返すコマンドであり、if はその0/非0を見て分岐しているのです。この仕組みを知ると、if grep -q error log.txt; then ... のように、test を使わずコマンドの成否そのもので分岐させる書き方も理解できます。なお bash には [ ] を強化した [[ ]] という書き方もあり、こちらは空変数でも式が壊れにくく、&&(かつ)や ||(または)を条件の中に直接書け、== によるパターン一致も使えるという利点があります。bash で書くと決まっている環境なら [[ ]] のほうが事故が少なく、どのシェルでも動く移植性が要るなら [ ] を選ぶ、という使い分けが実務の目安です。最後によくある失敗を挙げておくと、角カッコ前後の空白の入れ忘れ、数値比較に = を使ってしまう取り違え、変数のクオート漏れによる空変数での式崩れ——この3つが代表格です。条件分岐がうまく動かないときは、まずこの3点を疑うと早く原因にたどり着けます。

この項目に出てくる用語

test / [てすと
条件が真か偽かを判定するコマンド。[ ... ] とも書ける。
終了ステータスしゅうりょうすてーたす
コマンド終了時に返す0〜255の整数。0が成功、0以外が失敗。

関連コマンド

test / [echo

▶ 学習アプリでこの続きを学ぶ・演習する