bashの使い方:条件分岐の話
if / while / until / case の話をしようと思っています。とても大変なので、できるだけシンプルにストーリーにしてわかりやすく書こうと思っています。
最初は条件分岐の話です。条件分岐はbashの理解に大きな影響を与える話なのですが、書籍でも皆によく読み飛ばされてて残念な感じ。わたしも読み飛ばしてて後で痛い目にあった。
条件分岐の話をうまく書けてるかどうかは、bashの紹介記事や書籍の出来を大きく左右すると思います。
条件分岐以前の話
こんな話が出てきます。
- 終了ステータス・コード
- && による条件分岐
- || による条件分岐
- if を使わない複数行条件分岐
条件分岐 if 以前の話
bash でif条件分岐をするまえに、知っておくべき必須知識があります。
シェルの条件分岐は終了ステータスで判断するということです。
シェルがコマンドを実行すると、実行結果は終了ステータス・コード
で返されます。true / falseではありません。
他のプログラミング言語が true/false で判断するの対し、シェルでは終了ステータスが0か否かで判断されます。
大事なので2回言いました。
終了ステータスについて
終了ステータスは、0かそれ以外で判断されます。
終了ステータス | 終了で意味 | 条件での意味 |
---|---|---|
0 | 正常終了 | true |
1 | 異常終了 | false |
2 | 異常終了 | false |
- | 中略 | false |
N | 異常終了 | false |
0
は 正常終了で、正常終了なのでtrue と判断されます。0以外
は異常終了です。通常は正の整数で表現されます。
とくに、1がFalseで0がTrue になるところは特徴的なので忘れないでください。絶対に忘れないでください。
終了ステータス | 終了で意味 | 条件での意味 |
---|---|---|
N == 0 | 正常終了 | true |
N >0 | 異常終了 | false |
終了ステータスの確認
終了ステータスは、$?
の変数で確認できます。。 $?
に直前に実行されたコマンドの実行結果が格納されます。
つねに終了ステータス0を返す /bin/true を用いて調べてみましょう。
この1行で書く書き方を覚えておいてください。2行で書くのは不便なのでここから先は1行で書きます。
例:正常終了(1)
$ /usr/bin/true $ echo $? #=> 0
例:正常終了(2)・・・1行記述
$ /usr/bin/true ; echo $? #=> 0
例:異常終了
$ /usr/bin/false; echo $? #=> 1
例:コマンドが見つからない
$ NoExistCommand ; echo $? -bash: NoExistCommand: コマンドが見つかりません 127
終了ステータスを確認する方法を覚えておく。
紹介した終了ステータスを覚えておく方法は、覚えておくと便利です。
次の形で確認するとお覚えておくといいでしょう。
$ COMMAND ; echo $?
シェルスクリプトでif
の条件がただしいか。これらの目的のため、切り出して確認することも多いです。
このようにシェルスクリプトでは、if の中身だけを実行することが可能です。
例:if 条件を確認する
$ [[ $name = takuya ]] ; echo $?
原始的な条件分岐&&
と||
まだif は使いません。
&&
で 終了ステータスを活用する AND条件を書くことが出来ます。
一番シンプルな条件分岐が &&
による条件分岐です。今回はif の条件分岐に入る以前の話です。if は使いません。
&&
だけでも十分なコマンドの分岐を書くことが出来ます。
&& と終了ステータスを使って、コマンドを続けて実行する方法を見ておきます。
COMMAND1 && COMMAND2
これは、COMMAND1
が実行され、正常終了(戻り値0)になったら、COMMAND2
が実行されます。
また、 COMMAND
は 複数行で構成することもできるので { }
を使って次のように書くことも出来ます。
COMMAND1 && { COMMAND2-1; COMMAND2-2; }
次のサンプルでは、ディレクトリの *.jpg
を列挙して移動します。これはjpg ファイルがない場合で、ls *.jpg
が失敗すると、&&
以降 のコマンドは実行されません。。
この書き方は testコマンドと併せて使うと便利です。この程度なら、まだ if
無くても大丈夫です。
test コマンドは、条件分岐のチェックパターンでまた紹介します。
例:&& でコマンド組み合わせる
ls *.jpg && mv *.jpg /Users/takuya/Pictures
例:&& とtestの組み合わせ。
# ファイルが有れば中身を表示する test -e /etc/passwd && cat /etc/passwd
||
でコマンドをOR実行する
OR でコマンドを実行することが出来ます。使い方は次のとおりです。
COMMAND3 || COMMAND4
読み方としては、COMMAND3
を実行し、成功したら次の行へ、失敗したらCOMMAND4
を実行して次の行へ。という意味です。
繰り返しになりますが、ここで AND / OR と呼んでいるのは「終了ステータス」のAND・ORです。
ファイルなければ作る
test -e my-sample.txt || touch my-sample.txt
||
失敗したら処理をやめる
||
を使えば「失敗したら処理をやめる」ということ出来ます
takuya ユーザーがいなければexitして処理をやめる
id takuya || exit
まとめ
if の条件を考えるまえに、以下のことを覚えておくと、スッキリします。迷わずに住みます。
- シェルスクリプト(bash)において「条件分岐」とは「終了ステータスで判断する」
- 0/1 の考え方がプログラミング言語とは逆になっている。
- コマンドが成功したら次のコマンド
- 条件とはコマンドである。
- 通常のプログラミング言語を触ってる方々は「戻り値」を考えてしまうのでわからなくなる。
終了ステータスで判断するので、かんたんな条件ならif 無しで書くことが出来ます。また、そのほうがわかりやすいこともあります。
コマンドの終了ステータスコードで「判断する」は大事なので絶対覚えておいてください。
大事なので10回位書きました。
もちろん if を書いたほうが読みやすいので、if を使うほうが良いのでしょうが、if をいきなり使ってしまうと「何を以てTRUEか」という事を見逃しがちです。
&&
と||
で分岐は出来ます。敢えて if を使わないで十分な条件分岐が出来ることを紹介しました。
おまけ
if 文を使わずとも、かんたんな条件分岐なら、 &&
と { }
を使って書くことが出来ます。
test -e /etc/passwd && { echo /etc/passwd は存在しました; echo ファイルの末尾を表示します; tail /etc/passwd ;}
さらにこれは、{ }
内部で改行が許されるので、次のようにかけます。
test -e /etc/passwd && { echo /etc/passwd は存在しました; echo ファイルの末尾を表示します; tail /etc/passwd ; }
次の bash の条件分岐 if の記事では、この改行を含めたコードを、 「プログラミング」っぽく if を使うようにしておきたいと思います。
おまけ
この記事で紹介した終了ステータスを使ってANDでみることは、bashrcなどで盛んに行われます。
例:takuyaのbashrc からの抜粋
[ -z "$PS1" ] && return [ -f ~/.bash_aliases ] && source ~/.bash_aliases (( $BASH_VERSINFO >= 4 )) && shopt -s globstar
コピペして肥大化したbashrcをよく見るといたるところで使われていると感じると思います。