はじめに
シェルスクリプトは本来、三項演算子というものがありません。
三項演算子のような書き方はできるのですが、注意しないとハマって時間を溶かす可能性があるので気をつけましょう。
実行環境: Bash
TL;DR
シェルスクリプトの三項演算子もどきで、&&
の次に書くコマンドは必ず成功するコマンドを書くべきです。
失敗する可能性があるなら、$(your_command;:)
のように書き換えるか、素直にif文を使いましょう。
三項演算子っぽい書き方
繰り返し言いますが、シェルスクリプトには三項演算子はありません。
三項演算子もどきです。
これを下のように書くことができます。
command1 && command2 || command3
注意すべきポイントは、&&
や||
という演算子です。
&&
、||
の動作
&&
と||
の動作は下の通りです。
&& : 直前に実行されたコマンドが成功したとき、次のコマンドを実行
|| : 直前に実行されたコマンドが失敗したとき、次のコマンドを実行
直前に実行されたコマンドが成功したかどうかが重要なのです。
では、次の例を見てみましょう。
例
true && false || echo "failed"
# 実行結果:
# failed
true
コマンドは成功ステータスを返す(=必ず成功する)コマンド、
false
コマンドは失敗ステータスを返す(=必ず失敗する)コマンドです。
これを実行すると、failedと表示されます。
1つ目のコマンドが成功しているにも関わらずです。
これは、&&
や||
が直前に実行したコマンドの終了ステータスを見ていることに由来します。
上の例では、true
は成功するので、次のfalse
も実行されますが、false
は失敗ステータスを返してしまうので、||
の次のechoも実行されてしまうのです。
対策
上に示したように、&&
、||
は直前のコマンドが成功したかどうかで判定しているので、シェルスクリプトの三項演算子もどきは、C言語などの三項演算子とは根本的に動作が違います。
2つ目のコマンドが失敗したときは3つめのコマンドも実行されてしまうので、注意が必要です。
これを鑑み、正しく動作させるためには、2つ目のコマンドは必ず成功させないといけません。
失敗する可能性があるコマンドは入れないようにしましょう。
失敗する可能性があるコマンドをどうしても使う場合は、次のように書けば大丈夫かと思います。
command1 && $(command2 ; true) || command3
または
command1 && $(command2;:) || command3
失敗しそうなら成功ステータスを返せばいいじゃない、とマリーアントワネットになった気持ちで上のように書き換えましょう。
:
コマンドはtrue
と同じように成功ステータスを常に返します。よって、2つ目のコマンドが失敗したとしても、3つ目のコマンドは実行されずに済みます。
(ただし、このような対策は可読性が低くなるのが難点)
おわりに
シェルスクリプトの三項演算子もどきは気をつけないと想定外の動きをするので気をつけましょう。
個人的には、if文を使えばこういった間違いが無くなるのでif文を使ってます。