はじめに(対象読者・この記事でわかること)
この記事は、Linux/macOSのターミナルでBashスクリプトを書く機会がある方を対象にしています。特に「if文の条件内でコマンドの実行結果を評価したいけど、バッククォートがうまく動かない」という悩みを持つ方におすすめです。
記事を読むことで、if文の条件式内でバッククォート(または$())を使ったコマンド置換を正しく書く方法がわかります。また、シェルが条件式を解釈する仕組みや、よくあるエラー(「コマンドが見つかりません」など)の原因と対処法も身につきます。サンプルコードをそのままコピペして動かせるので、すぐに業務や開発に活用できます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Bashで単純なif文を書いたことがある(例: if [ -f file ]; then ...)
- コマンド置換の基本概念(バッククォート`command`または$()でコマンドの結果を取得する)
- テストコマンド[(または[[)と演算子(-eq、-ne、-gtなど)の違いをある程度知っている
if文の条件内でコマンド置換を使いたいときの落とし穴
Bashスクリプトを書いていて「コマンドの実行結果を数値として取得し、それをif文で比較したい」と思ったことはありませんか? たとえば、稼働中のDockerコンテナ数がしきい値を超えているかチェックしたいとき、次のように書いて「なんか動かない」とハマるケースが多発します。
Bash# 間違い例:条件文の中でバッククォートをそのまま書く if [ `docker ps -q | wc -l` > 5 ]; then echo "コンテナが多すぎます" fi
このスクリプトを実行すると、「[: 10: コマンドが見つかりません」のような不可解なエラーが出るか、比較結果が常に偽になってしまいます。原因は、シェルが>をリダイレクトとして解釈してしまうことにあります。条件式を正しく書くには、コマンド置換の結果を数値として評価する(( ))または、文字列として比較する[[ ]]を使い分ける必要があります。
正しい構文と実装パターン
以下、実装でよく使う3つのパターンを紹介します。どれもコマンド置換の結果を安全に評価できるように、適切な演算子とクォートを組み合わせています。
パターン1:数値比較には(( ))を使う
Bash# 例:稼働コンテナ数が5超かチェック if (( $(docker ps -q | wc -l) > 5 )); then echo "コンテナが多すぎます" fi
(( ))は算術評価専用の構文で、内部の変数やコマンド置換は自動で数値として解釈されます。比較演算子はC言語風(>, <, ==, >=など)を使えるため可読性も高いです。文字列中の空白や特殊文字を気にしなくて済むのも大きな利点です。
パターン2:文字列比較には[[ ]]と$()を組み合わせる
Bash# 例:Gitの現在のブランチ名が"main"かチェック if [[ "$(git branch --show-current)" == "main" ]]; then echo "mainブランチにいます" fi
[[ ]]は文字列比較に特化した条件式で、正規表現マッチも可能です。コマンド置換の結果をダブルクォートで囲むことで、ブランチ名に空白が含まれていても安全に評価できます。==の代わりに=~を使えば、正規表現でのマッチングも簡単に行えます。
パターン3:旧環境互換が必要ならテストコマンドと$()を使う
Bash# 例:ファイル数が10以上か(POSIX sh互換) count=$(find ./log -type f -name "*.log" | wc -l) if [ "$count" -ge 10 ]; then echo "ログファイルが多すぎます" fi
古いシェルや/bin/sh互換が必要な場合は、[コマンドを使います。コマンド置換の結果を一旦変数に入れ、ダブルクォートで囲って比較します。数値比輩の演算子は-ge(以上)、-le(以下)、-eq(等しい)などを使います。可読性はやや下がりますが、どの環境でも動作する保守的な書き方です。
ハマった点やエラー解決
筆者が実際にハマったのは、「コマンド置換の結果をそのまま[ ]で評価しようとした」ケースです。次のスクリプトを書いたところ、「[: too many arguments」というエラーが出ました。
Bash# 間違い例:コマンド結果をそのまま[ ]で評価 if [ $(find . -name "*.tmp" | wc -l) -gt 0 ]; then rm *.tmp fi
原因は、wc -lの結果が" 0"のように空白を含んでいて、[ ]内で単語分割されてしまうことでした。空白を含む文字列を評価する際は、ダブルクォートで囲むか、[[ ]]/(( ))を使う必要があります。
解決策
以下のように書き換えることで、意図通りに動作しました。
Bash# 解決策1:ダブルクォートで囲う count=$(find . -name "*.tmp" | wc -l) if [ "$count" -gt 0 ]; then rm *.tmp fi # 解決策2:(( ))で数値評価 if (( $(find . -name "*.tmp" | wc -l) > 0 )); then rm *.tmp fi
数値を扱う場合は(( ))が最も簡単で安全です。文字列やファイル名を扱う場合は[[ ]]を使い、ダブルクォートで囲って空白を考慮する習慣をつけると、多くの落とし穴を回避できます。
まとめ
本記事では、Bashのif文の条件式内でバッククォート/コマンド置換を使う正しい方法を紹介しました。
- 数値比較:
(( $(command) > 5 ))が最も簡単で安全 - 文字列比較:
[[ "$(command)" == "value" ]]で空白も安全 - 旧環境互換:一旦変数に入れて
[ "$var" -ge 10 ]と書く
この記事を通して、コマンド置換の結果を条件に使う際の「なぜ動かない」の原因がわかり、シェルスクリプトの保守性と可読性が高まるでしょう。次回は、条件式でよく使う正規表現マッチや、より複雑な条件を短く書くテクニックについて掘り下げていく予定です。
参考資料
- Bash Reference Manual §6.5 Conditional Constructs
- Bash Hackers Wiki §Tests and Conditionals
- ShellCheck – Shell script analysis tool
