Bashスクリプトでは、条件文が失敗したり予期せぬ動作をしたりすると、本当に困った問題になることがあります。必ずしも構文ミスが原因とは限りません。適切な引用符の付け忘れ、演算子の混同、あるいはテスト構文の誤りといった小さな問題が原因となることも少なくありません。こうした小さなエラーによって、スクリプトが間違ったブランチを実行したり、重要なチェックをスキップしたり、あるいは明確な兆候もなく動作が停止したりすることがあります。少し奇妙に感じるかもしれませんが、よくある落とし穴を知っておくと、信頼性の高い条件文を書くのがずっと簡単になります。さらに、環境によっては、最初は失敗したスクリプトが、再起動やシェルの再起動で動作する場合もあります。WindowsやLinuxは、こうした問題に必ずしもうまく対応しているわけではありません。
このガイドでは、Bashの条件文をより簡単に記述するための、実証済みの方法をいくつか解説します。 のような現代的な構文の使い方[[ ]]
、 を使ったより簡潔な数式処理(( ))
、 を使った移植性の向上[ ]
、さらには を使ったロジックの統合方法も学びますcase
。これらを正しく実行すれば、スクリプトはスムーズに実行され、予期せぬ問題も発生しなくなります。さらに、コードの問題なのかシェルの不具合なのか分からず頭を悩ませる、そんなイライラする瞬間を回避できます。
うまく動作しないBashの条件文を修正する方法
方法1: if...else
Bashで書く[[ ]]
この[[ ]]
構文は、以前の のアップグレード版といったところでしょうか[ ]
。より寛容で、誤って単語を分割したりワイルドカードを使用したりといった問題を防ぎ、全体的に予測可能な動作をします。特に奇妙なバグを避けたい場合、現代のbashスクリプトには必須のツールです。
簡単な例を以下に示します。
#!/usr/bin/env bash set -euo pipefail read -r -p "Enter a value: " val if [[ "$val" == "admin" ]]; then echo "Welcome, admin." else echo "Access limited." fi
文字列マッチングに使用されている方法に注目してください==
。変数は必ず引用符で囲んでください。たとえ単語1つだけの場合でも、スペースや空文字列による問題を防ぐことができます。ちょっとした習慣ですが、多くの問題を軽減してくれます。
文字列と数値を安全に比較する
user="alice" target="alice" if [[ "$user" == "$target" ]]; then echo "Usernames match." else echo "Usernames differ." fi # Numeric comparison: read -r -p "Enter a number: " n if [[ "$n" -gt 10 ]]; then echo "Greater than 10." elif [[ "$n" -eq 10 ]]; then echo "Equal to 10." else echo "Less than 10." fi
これらのテストでは、空の文字列やスペースによる混乱を避けるため、変数を引用符で囲むようにしてください。数値の比較には、従来のシェルスクリプトと同様に-gt
、-eq
、 などを使用します。
ファイルの存在と権限
path="./data.txt" if [[ -f "$path" ]]; then echo "File exists." else echo "Creating file..." : > "$path" # creates an empty file fi
これは、ファイルが存在するかどうかを確認し、存在しない場合はファイルを作成するのに役立ちます。設定を初期化したり、ファイルを処理したりするスクリプトに役立ちます。
複数の条件を&&
andで組み合わせる||
file="./report.log" size=5 if [[ -f "$file" ]] && [[ "$size" -ge 5 ]]; then echo "Process report." fi role="staff" if [[ "$role" == "admin" || "$role" == "staff" ]]; then echo "Privileged access granted." fi
このきちんとした構文により、誤って括弧を追加しすぎたり、スペースを忘れたりした場合に発生するエラーを防止できます。シェルはこの点に敏感です。
elif
複数のチェックに使用する
status="warning" if [[ "$status" == "error" ]]; then echo "Exit immediately." exit 1 elif [[ "$status" == "warning" ]]; then echo "Log and continue." else echo "All good." fi
これは、ネストされたif文をたくさん書くよりもずっと簡潔です。スクリプトの可読性を維持し、バグの発生を抑えるのに役立ちます。
方法2:数学を使って(( ))
スクリプトが整数のみを扱う場合、(( ))
比較と計算をより簡潔に処理する方法です。よりクリーンで高速なだけでなく、より一般的な数式に近い表現になります。
数学の比較の例:
a=12 b=8 if (( a > b )); then echo "a is larger." elif (( a == b )); then echo "Equal." else echo "b is larger." fi
複数の数値条件がある場合は、&&
または||
を の中に連結するだけです(( ))
。テストを複数回実行するよりもはるかに効果的です。
x=7 y=3 z=10 if (( (x > y) && (z >= 10) )); then echo "Threshold met." fi
注: 入力検証では、読み取った内容が実際に数値であるかどうかを常に確認してください。特にユーザー入力が関係する場合は重要です[[ "$n" =~ ^-?[0-9]+$ ]]
。
方法3: POSIXで移植性を保つ[ ]
スクリプトを厳密なPOSIXシェルや/bin/shで実行する必要がある場合は、 を使用してください[ ]
。これは古いバージョンですが、依然として信頼性があります。
#!/bin/sh a="hello" b="hello" if [ "$a" = "$b" ]; then echo "Match." else echo "No match." fi # Numeric comparison: a=5 b=30 if [ "$a" -lt "$b" ]; then echo "a is less than b." fi # String lex order: x="apple" y="banana" if [ "$x" \< "$y" ]; then echo "apple comes before banana." fi
POSIXシェルで文字列比較を行う場合は、小なり記号をエスケープしてください。そうしないとシェルが混乱する可能性があります。奇妙に思えますが、POSIXではこのように処理されます。
方法4:case
文を使って簡素化する
単一の変数が複数の値を持つことができる場合、 のほうが、case
一連の よりも分岐をよりきれいに処理する方法ですif
。
read -r -p "Enter mode (start|stop|status): " mode case "$mode" in start) echo "Starting...";; stop) echo "Stopping...";; status) echo "Service is running.";; *) echo "Unknown mode."; exit 1;; esac
複数の一致の場合:
level="warn" case "$level" in error|err) echo "Exit with failure."; exit 1;; warn|warning) echo "Log warning.";; info|debug) echo "Proceed normally.";; *) echo "Unrecognized level.";; esac
方法5: デバッグとベストプラクティス
- 複雑な条件は括弧でまとめ、わかりやすくします。必須ではありませんが、整理するのに役立ちます。
==
文字列には 、整数には を使用し-eq
、変数には常に引用符を付けます。- 何かがうまくいかないときにデバッグをオンにすると
set -x
、実行時に各コマンドが表示されるので、どこで問題が発生しているかを見つけることができます。
set -x # turn on tracing # your conditions here set +x # turn it off when done
- シェルが気にしない場合でも、インデントの一貫性を保つようにしてください。そうすることで、後で読みやすくなり、バグを修正しやすくなります。
クイックオペレーターチートシート
- 文字列:
==
、、(空でない)、!=
(空)-n var
-z var
- 数値:
-eq
、、、、または構文を使用します-ne
。-gt
-lt
(( ))
- ファイル:
-f
(存在する通常のファイル)、-d
(ディレクトリ)、-e
(存在する)、-x
(実行可能ファイル)など。 - 論理:
&&
(そして)、||
(または)、!
(ない)
まとめ
基本的に、Bash の条件分岐に関するトラブルのほとんどは、テスト構文の誤り、演算子の誤り、引用符の抜けなどに起因します。[[ ]]
ほとんどの処理には を使い、(( ))
数式処理には を使い、[ ]
上位互換性が必要な場合は を使用します。デバッグと入力検証を少し追加するだけで、スクリプトの予測可能性が大幅に高まります。重要なのは、良い習慣を身につけ、各テストが内部で何をしているのかを理解することです。
まとめ
[[ ]]
文字列とファイルのテストに使用します。より安全で最新です。- 数学に使用します
(( ))
– 整数のよりクリーンな構文。 - 予期せぬ事態を避けるために、常に変数を引用符で囲みます。
case
単一の変数に基づく複数の分岐に使用します。- 何か問題が起こった場合は必ずデバッグをオンにしてください (
set -x
)。
これで、bashスクリプトのデバッグに取り組んでいる人の時間を数時間短縮できれば幸いです。確かにシェルスクリプトは扱いにくいものですが、これらのテストを正しく実行する方法さえ分かれば、ストレスは大幅に軽減されます。