はじめに (対象読者・この記事でわかること)

この記事は、Bashスクリプトを記述していて「単項演算子が予期されます (unary operator expected)」というエラーに遭遇したことがある方、またはこれから遭遇する可能性のある方を対象としています。特に、数値比較や条件分岐を記述する際にこのエラーが発生しやすい傾向があります。

この記事を読むことで、以下のことがわかるようになります。

  • 「単項演算子が予期されます」エラーが発生する主な原因
  • 具体的なエラー発生箇所と、それを特定する方法
  • エラーを回避するための正しい記述方法と、いくつかの解決策
  • Bashスクリプトにおける数値比較のベストプラクティス

「単項演算子が予期されます」エラーは、Bashスクリプトのデバッグにおいて頻繁に遭遇する問題の一つですが、その原因を正確に理解し、正しい対処法を身につけることで、スムーズなスクリプト開発が可能になります。

Bashスクリプトにおける数値比較の落とし穴

Bashスクリプトで条件分岐を行う際、if文と角括弧[]または二重角括弧[[]]を使用して条件を評価するのが一般的です。特に、数値の大小比較や等価性比較を行う場合、いくつかの注意点があります。

「単項演算子が予期されます」エラーとは?

このエラーメッセージは、Bashが条件式の中で予期しない単項演算子(例: +, -, ! など)を見つけたことを示しています。しかし、実際には単項演算子そのものが問題なのではなく、条件式に使用している演算子やオペランドの記述方法に誤りがあることが原因です。

エラー発生の典型的なシナリオ

このエラーが頻発する典型的なケースは、数値比較演算子(-eq, -ne, -lt, -le, -gt, -ge)の代わりに、文字列比較演算子(=, !=)を数値に対して使用しようとした場合や、比較演算子を囲む角括弧の使い方が間違っている場合です。

例えば、以下のようなコードはエラーを引き起こす可能性があります。

Bash
count=0 if [ $count = 0 ]; then # 文字列比較演算子 "=" を数値比較に使用 echo "count is zero." fi

この場合、Bashは $count の値 0 と文字列 "0" を比較しようとしますが、= は本来文字列比較のための演算子であり、数値比較の文脈で使われると予期しない動作を引き起こすことがあります。さらに、[ ] の中では、数値比較演算子も文字列として解釈される可能性があり、混乱を招きます。

また、変数展開が正しく行われず、空文字列や予期しない文字が比較演算子のオペランドとして渡された場合も、このエラーが発生しやすくなります。

[ ][[ ]] の違いと数値比較

Bashでは、条件式を記述するために [ (または test コマンド) と [[ ]] がよく使われます。

  • [ ] (または test):

    • Posix標準に準拠しており、より多くのシェルで互換性があります。
    • 式の中では、変数展開やコマンド置換が行われた後、最終的に文字列として評価されます。
    • 数値比較には、-eq, -ne, -lt, -le, -gt, -ge などの 整数比較演算子 を使用する必要があります。
    • = は文字列比較演算子です。
    • 変数が空の場合、エラー(例: [: =: unary operator expected)が発生しやすいため、変数をダブルクォートで囲む ("$variable") ことが重要です。
  • [[ ]]:

    • Bash独自の拡張機能であり、より柔軟で強力な機能を提供します。
    • 式の中では、より洗練されたパースが行われ、パターンマッチング正規表現 も直接使用できます。
    • 数値比較演算子 (-eq など) と文字列比較演算子 (=) を混在させて使用しても、Bashが文脈を判断してくれる場合が多いですが、意図しない動作を避けるためにも、明確に使い分けることが推奨されます。
    • 論理演算子 (&&, ||) を直接使用できます。
    • 変数が空の場合でも、エラーが発生しにくい傾向があります。

具体的なエラー例とその原因

例1:数値比較に文字列比較演算子 (=) を使用

Bash
#!/bin/bash num1=10 num2=10 if [ $num1 = $num2 ]; then # 誤: 数値比較に "=" を使用 echo "num1 is equal to num2 (string comparison)" fi # 実行結果: # num1 is equal to num2 (string comparison) # (この場合は文字列比較として意図通り動くこともあるが、数値比較のつもりだと危険) # 意図しないエラーが発生する例 num1=10 num2=5 if [ $num1 = $num2 ]; then # 誤: 数値比較に "=" を使用 echo "num1 is equal to num2" else echo "num1 is not equal to num2" fi # 実行結果: # num1 is not equal to num2 # さらに、以下のような場合でエラーが発生しうる if [ $num1 = ]; then # num2 が未定義などで空の場合 echo "This will cause an error." fi # 実行結果: # bash: [: =: unary operator expected

例2:比較演算子を囲む括弧の誤り

Bash
#!/bin/bash count=1 # if count -eq 1; then # 誤: 括弧がない # echo "count is 1" # fi # 実行結果: # bash: count: command not found (これは別のエラーだが、類似の文脈で単項演算子エラーもありうる) # if [count -eq 1]; then # 誤: 変数展開を考慮していない # echo "count is 1" # fi # 実行結果: # bash: [count: command not found (これも別のエラー。代入等で発生) # 意図しないエラー発生例 if [ $count -eq 1 ]; then # 正しい記述だが、もし $count が未定義なら echo "count is 1" fi # $count を空にして実行した場合 count="" if [ $count -eq 1 ]; then echo "count is 1" else echo "count is not 1" fi # 実行結果: # bash: [: : integer expression expected (これもエラーだが、文脈によっては unary operator expected になる)

「単項演算子が予期されます」エラーの具体的な解決策

このエラーを解決するには、Bashスクリプトの条件式の記述方法を正しく理解し、誤りを修正する必要があります。以下に、具体的な解決策をいくつか紹介します。

解決策1:数値比較には整数比較演算子を使用し、[[]] で囲む

最も推奨される方法の一つは、[[ ]] を使用し、数値比較には -eq, -ne, -lt, -le, -gt, -ge といった 整数比較演算子 を明示的に使用することです。また、変数は ダブルクォートで囲む ことが、予期せぬエラーを防ぐための重要な習慣です。

Bash
#!/bin/bash # 例: 変数 count が 0 より大きいかどうかのチェック count=5 if [[ $count -gt 0 ]]; then echo "Count is greater than 0." else echo "Count is 0 or less." fi # 例: 変数 num1 と num2 が等しいかどうかのチェック num1=10 num2=10 if [[ $num1 -eq $num2 ]]; then echo "num1 is equal to num2." else echo "num1 is not equal to num2." fi # 例: 変数 status が 1 でないかどうかのチェック status=0 if [[ $status -ne 1 ]]; then echo "Status is not 1." else echo "Status is 1." fi

[[ ]] を使うメリット:

  • [[ ]] の内部では、Bashがより文脈を理解して処理するため、[ ] よりもエラーが発生しにくいです。
  • 文字列比較 (=) と数値比較 (-eq など) が混在しても、比較的安全に動作します(ただし、意図を明確にするために使い分けるのがベストです)。
  • 変数展開で空文字列になっても、エラーになりにくいです。

解決策2:[ ] を使用する場合の注意点と対策

もし、Posix互換性を重視して [ ] を使用する必要がある場合は、より一層の注意が必要です。

  1. 数値比較には整数比較演算子 (-eq, -ne, -lt, -le, -gt, -ge) を使用する。
  2. 変数は必ずダブルクォートで囲む ("$variable")。
  3. 比較演算子の左右には必ずスペースを入れる。
Bash
#!/bin/bash # 例: 変数 count が 0 より大きいかどうかのチェック count=5 if [ "$count" -gt 0 ]; then # 正しい記述 echo "Count is greater than 0." else echo "Count is 0 or less." fi # 例: 変数 num1 と num2 が等しいかどうかのチェック num1=10 num2=10 if [ "$num1" -eq "$num2" ]; then # 正しい記述 echo "num1 is equal to num2." else echo "num1 is not equal to num2." fi # エラーになる例 (以下は "$count" が未定義または空の場合にエラーになる) # if [ $count -eq 1 ]; then ... # if [ $count = 1 ]; then ... (文字列比較として認識されない可能性)

[ ] を使う際の注意点:

  • 変数が未定義または空の場合、[ "$variable" -eq ... ]$variable が展開されず、[ -eq ... ] のようになり、「: が予期せぬ単項演算子」や「integer expression expected」といったエラーが発生する可能性があります。
  • [ ] の中で == を使うと、一部のシェルではパターンマッチングとして解釈されることがありますが、Posix標準では = を使うべきです。

解決策3:let コマンドや算術展開 (( )) を活用する

Bashには、算術演算を安全に行うための let コマンドや算術展開 (( )) も用意されています。これらを使用すると、数値比較をより簡潔かつ安全に行うことができます。

  • let コマンド: let コマンドは、引数で与えられた算術式を評価します。

    ```bash

    !/bin/bash

    count=5

    let "count > 0" # let コマンドは、式が真なら0、偽なら1を返す if [ $? -eq 0 ]; then # $? は直前のコマンドの終了ステータス echo "Count is greater than 0." fi

    num1=10 num2=10 let "num1 == num2" if [ $? -eq 0 ]; then echo "num1 is equal to num2." fi ``letコマンドは、終了ステータスで結果を返すため、if [ $? -eq 0 ]` のようにチェックする必要があります。

  • 算術展開 (( )): (( )) は、Bashの算術式評価のための構文です。この中で行われる比較演算子の結果は、真なら 1、偽なら 0 という終了ステータスとして返されます。

    ```bash

    !/bin/bash

    count=5

    if (( count > 0 )); then # 算術展開を使用 echo "Count is greater than 0." else echo "Count is 0 or less." fi

    num1=10 num2=10

    if (( num1 == num2 )); then # "==" を使っても問題ない (C言語ライクな比較) echo "num1 is equal to num2." else echo "num1 is not equal to num2." fi

    num3=20

    if (( num1 = num3 )); then # 代入文は終了ステータス 0 になるため、意図しない結果に

    echo "This will always be printed if num3 is defined."

    fi

    比較演算子で適切に記述

    if (( num1 < num3 )); then echo "num1 is less than num3." fi ```

(( )) を使うメリット:

  • 変数をダブルクォートで囲む必要がありません。
  • = ではなく == で比較することも可能です(C言語ライクな記法)。
  • 変数が未定義でも、数値の 0 として扱われるため、エラーが発生しにくいです。
  • 算術演算(加減乗除など)も直接記述でき、非常に便利です。

解決策4:デバッグツール set -x を活用する

スクリプトのどこでエラーが発生しているのか特定できない場合、set -x コマンドをスクリプトの冒頭や、問題が発生している可能性のある箇所の手前に挿入すると、実行されるコマンドが標準エラー出力に表示されるようになります。これにより、どの行で「単項演算子が予期されます」エラーが発生しているかを正確に把握し、問題のあるコードを特定するのに役立ちます。

Bash
#!/bin/bash set -x # これを追加すると、実行されるコマンドが表示される count=0 # ここでエラーが発生すると仮定 if [ $count = 0 ]; then # ここが誤りだと仮定 echo "count is zero." fi set +x # デバッグモードを終了する場合

set -x を有効にした状態でスクリプトを実行すると、以下のように表示され、問題の行が特定しやすくなります。

+ count=0
+ '[' 0 = 0 ']'  <-- この行でエラーが発生していることがわかる
+ echo 'count is zero.'
count is zero.

修正前後の比較

【修正前】

Bash
#!/bin/bash num=5 if [ $num = 5 ]; then # 数値比較に文字列比較演算子 "=" を使用 echo "num is 5" fi

このコードは、$num"5" という文字列と 5 という数値(または文字列 "5")を比較するため、意図しない結果になったり、文脈によってはエラーになったりします。

【修正後([[ ]]-eq を使用)】

Bash
#!/bin/bash num=5 if [[ $num -eq 5 ]]; then # 数値比較演算子 "-eq" と [[ ]] を使用 echo "num is 5" fi

こちらの方が、数値比較として明確であり、エラーが発生するリスクが低減されます。

【修正後((( )) を使用)】

Bash
#!/bin/bash num=5 if (( num == 5 )); then # 算術展開 "(( ))" と "==" を使用 echo "num is 5" fi

この方法も、安全かつ簡潔に数値を比較できます。

まとめ

本記事では、Bashスクリプトで頻繁に遭遇する「単項演算子が予期されます (unary operator expected)」エラーの原因と、その解決策について詳しく解説しました。

  • エラーの主な原因: 数値比較に文字列比較演算子を使用したり、角括弧 [ ] や変数の扱いを誤ることが原因です。
  • 推奨される解決策:
    • [[ ]] と整数比較演算子 (-eq など) を使用する
    • 算術展開 (( )) を活用する
  • [ ] を使用する場合: 変数は必ずダブルクォートで囲み、整数比較演算子を明示的に使用することが重要です。
  • デバッグ: set -x コマンドは、エラー箇所の特定に非常に有効です。

この記事を通して、Bashスクリプトにおける数値比較の正確な記述方法を習得し、「単項演算子が予期されます」エラーに自信を持って対処できるようになることを目指しました。

今後は、Bashスクリプトにおけるより高度なエラーハンドリングや、実践的なスクリプト作成テクニックについても記事にする予定です。

参考資料