株式会社オズビジョンのユッコ (@terra_yucco) です。
監視スクリプトなどをはじめ、LAMP でサービスやっている企業でも Bash を書く機会はまだまだたくさんあります。
そんな中、複数条件で判断するとき、短絡評価なのか?非短絡評価なのか?が気になったので調べてみた。
短絡評価
短絡評価とは、
A == 真
B == 真
の命題があった場合において、
A || B
を判断する場合、B 側の判断を行わないような評価方法のことを言います。
A と B どっちかが真ならば条件を満たすので A が真である時点で B 側は見ない、非常に効率的です。
ちなみに &&
においても同様であり
A == 偽
B == 偽
の命題があった場合において、
A && B
を判断する場合、やはり B 側の判断は行われません。上記と同じロジックによるものです。
Bash における条件判断
実態は test
コマンドですが、スクリプト内では以下の書き方をすることが (少なくとも私は) 多いです。
#!/bin/bash -u
if [ ! -f /path/to/file ]; then
echo 'NG'
exit 1
fi
echo 'OK'
exit 0
複合条件の場合に短絡評価と非短絡評価をする方法を考えてみます。
特定の日だけファイルの行数をチェックし、それ以外の日は一律 OK とするような処理を例にします。
短絡評価
以下のようにそれぞれの条件式を []
で分割し &&
演算子でつなぐことで短絡評価になります。
#!/bin/bash -u
if [ $( date +%Y%m%d ) -eq 20190601 ] && [ $( cat /path/to/file | wc -l ) -eq 1 ]; then
echo 'NG'
exit 1
fi
echo 'OK'
exit 0
1 つ目の評価式に記載した date は実行されていますが、2 つ目の評価式に記載したファイルのチェックは行われていません。
$ bash -x minimal.sh
++ date +%Y%m%d
+ '[' 20190613 -eq 20190601 ']'
+ echo OK
OK
+ exit 0
非短絡評価
1 つの []
の中に評価式を入れ -a
でつなぐことで非短絡評価になります。
#!/bin/bash -u
if [ $( date +%Y%m%d ) -eq 20190601 -a $( cat /path/to/file | wc -l ) -eq 1 ]; then
echo 'NG'
exit 1
fi
echo 'OK'
exit 0
実際に cat
も呼び出され、エラーなども発生していることがわかります。
$ bash -x non-minimal.sh
++ date +%Y%m%d
++ cat /path/to/file
++ wc -l
cat: /path/to/file: そのようなファイルやディレクトリはありません
+ '[' 20190613 -eq 20190601 -a 0 -eq 1 ']'
+ echo OK
OK
+ exit 0
man test の記載
bash バージョン
$ bash --version
GNU bash, バージョン 4.2.46(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
man の該当項目
EXPRESSION1 -a EXPRESSION2
both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2
either EXPRESSION1 or EXPRESSION2 is true
これを見ると、評価が短絡かどうかは明記されていないので、もしかしたら系に依存するかもしれません。
Conclusion
[ EXPRESSION1 ] && [ EXPRESSION2 ]
は短絡評価
[ EXPRESSION1 -a EXPRESSION2 ]
は非短絡評価
手が覚えているレベルでよく使う Bash の評価式でしたが、この 2 つの書き方に評価の違いがあるということは調べてみて初めて知りました。