スクリプトを書く

INPUT · スライド

`if` で分かれる

01 / 08

前の回の1行を、読みやすく

前の回でこう書いたね。

[ $# -eq 0 ] && echo "kaite ne" && exit 1

動くけれど、やることが増えると読みにくくなるでしょう。3つも && が並ぶと、どこまでが条件か分からないよね。

同じことを、こう書けるんだ。

if [ $# -eq 0 ]; then  echo "kaite ne"  exit 1fi

条件と、やることが分かれたでしょう。これが if だよ。

読み方はこうだね。

if  もしthen  そのときはfi    ここまで

fiif を逆に書いたものだよ。「閉じる」の印なんだ。他の言葉の } と同じ役目だね。

そして分かれ道も書けるよ。

if [ 条件 ]; then  こちらelse  あちらfi

これでプログラムの3要素の2つめが手に入るんだ。

1. 順に実行する    ← 1本目でできた2. 条件で分かれる  ← 今日3. 繰り返す        ← 次の回

02 / 08

[ はコマンドの名前

ここがこの回でいちばん大事なところだよ。

~ $ ls /usr/bin | grep -x "\["[~ $ which test/usr/bin/test

[ という名前のファイルが本当にいるんだ! 記号ではなく、コマンドの名前なんだよ。

本名は test だよ。同じものだから、こう書いても動くんだ。

~ $ test -f memo.txt; echo $?0~ $ [ -f memo.txt ]; echo $?0

どちらも 0 を返したでしょう。この 0 が答えなんだ。

0        条件が成り立った(真)0 以外   成り立たなかった(偽)

そして if は、うしろのコマンドの終了ステータスを見て分かれているだけなんだ。

if コマンド; then …     ↓ そのコマンドを動かす     ↓ 0 で終わったら then のほうへ

つまり if は「条件式」を見ているのではなく、コマンドの成否を見ているんだよ。

だから [ のうしろに空白が要る理由も分かるでしょう。

[ -f memo.txt ]     ○ コマンドと引数[-f memo.txt]       × そんな名前のコマンドは無い

] まで引数なんだ。変な決まりに見えるけれど、[ という名前のコマンドが「最後は ] で閉じてね」と決めているだけなんだよ。

03 / 08

ファイルを調べる印

よく使う印を並べておくね。

成り立つとき
-fふつうのファイルが有る
-d入れ物(ディレクトリ)が有る
-e何かが有る(種類は問わない)
-s中身が空でない
-r読める
-w書ける
-x実行できる

使い方はこうだよ。

if [ -f "$1" ]; then  wc -l < "$1"else  echo "$1 ga nai"fi

動かす前に確かめるでしょう。これがあると、エラーの代わりに親切な言葉を出せるんだ。

章6でやった印と同じ名前が並んでいるね。

-r -w -x     章6の rwx と同じ

そのとおりで、自分がそれをできるかを調べているんだ。だから同じファイルでも、人によって答えが変わるよ。

-e-f の違いも見ておいてね。

-e hako     入れ物でも真-f hako     入れ物だと偽(ふつうのファイルではない)

「有るか」だけ知りたいなら -e、「読めるファイルか」まで見たいなら -f だね。-f のほうが厳しいんだ。

04 / 08

中身を比べる

文字と数では、書き方が違うんだ。

文字を比べる

[ "$1" = "aka" ]      同じ[ "$1" != "aka" ]     違う[ -z "$1" ]           空っぽ[ -n "$1" ]           空ではない

数を比べる

[ $# -eq 0 ]     等しい[ $# -ne 0 ]     等しくない[ $# -gt 2 ]     より大きい[ $# -lt 2 ]     より小さい[ $# -ge 2 ]     以上[ $# -le 2 ]     以下

名前は英語の略だよ。

eq  equal          等しいne  not equal      等しくないgt  greater than   より大きいlt  less than      より小さい

なぜ数だけ特別なのかというと、> が使えないからなんだ。

[ $# > 2 ]      これは「2 というファイルに書き出す」!

> はリダイレクトの記号だったでしょう。だから数の比べには別の名前を使うんだ。章4の知識が、こういう決まりの理由を説明してくれるね。

そして変数は必ず囲むよ。

[ "$1" = "aka" ]    ○[ $1 = "aka" ]      × 空のときに壊れる

$1 が空だと [ = "aka" ] になって、形が崩れてしまうんだ。囲めば [ "" = "aka" ] になって、ちゃんと偽になるでしょう。

05 / 08

3つ以上に分かれる

分かれ道が3つ以上あるときは elif を使うよ。

if [ "$1" = "aka" ]; then  echo akadesuelif [ "$1" = "ao" ]; then  echo aodesuelse  echo shiranaifi

elif は「else if」の略だね。上から順に調べて、最初に成り立ったものだけ動くよ。

~ $ ./b.sh akaakadesu~ $ ./b.sh kishiranai

並べ方にコツがあるよ。よく起きるものを上に書くんだ。

if [ よくある ]; thenelif [ ときどき ]; thenelse  それ以外fi

読む人が上から読むので、そのほうが分かりやすいでしょう。

else は無くてもいいよ。

if [ $# -eq 0 ]; then  exit 1fi

「あてはまらなければ何もしない」ときは、これで十分だね。書かなくてよいものは書かないのがきれいな書き方だよ。

条件を組み合わせることもできるよ。

[ -f "$1" -a -s "$1" ]      両方(and)[ -f "$1" -o -d "$1" ]      どちらか(or)[ ! -f "$1" ]               逆(not)

読みにくいときは && で分けてもいいよ。

[ -f "$1" ] && [ -s "$1" ]

こちらのほうが読みやすいと言われることが多いね。

06 / 08

コマンドをそのまま条件にできる

[ はコマンドだと言ったでしょう。だったら、他のコマンドも条件に書けるはずだよね。

書けるんだ。

if grep -q aka "$1"; then  echo mitsuketaelse  echo naifi

grep-q は「何も出さずに、見つかったかどうかだけ返す」という印だよ。

見つかった   →  0 で終わる  →  then のほう無かった     →  1 で終わる  →  else のほう

[ を使わない if でしょう。こちらのほうが素直に読めることも多いんだ。

if [ "$(grep -c aka "$1")" -gt 0 ]; then     まわりくどいif grep -q aka "$1"; then                    すっきり

他にもいろいろ書けるよ。

if mkdir hako 2>/dev/null; then     作れたらif [ -f a ] && cp a b; then         写せたら

ここで、この章のいちばん大事な考え方が出てくるよ。

> コマンドは、成功したか失敗したかを必ず返している

あなたが打ってきた全部のコマンドが、裏で 00 以外 を返していたんだ。ふだんは見えないけれど、echo $? で見られるんだったね。

これを本格的に扱うのが、この章の6本目だよ。今日は「if はそれを見ている」ということだけ持っていってね。

07 / 08

書き方の作法

if を書くときの形を決めておこうね。

if [ -f "$1" ]; then  wc -l < "$1"fi

;then の位置が独特でしょう。理由はこうだよ。

if [ -f "$1" ]thenfi

本当はこうなんだ。then は別の行に書くものなんだよ。でも縦に長くなるので、; でつないで1行にするのがふつうになったんだ。

; は「行を分ける」の代わり

中を2文字ぶん下げるのも作法だね。

if [ … ]; then  ここを下げるfi

下げなくても動くけれど、どこまでが if の中かが目で分かるでしょう。他の言葉でも同じことをするよ。

短いときは1行に詰めてもいいよ。

if [ -f "$1" ]; then wc -l < "$1"; fi

でも、こういうときは && のほうが短いね。

[ -f "$1" ] && wc -l < "$1"

使い分けはこうだよ。

やることが1つ      →  && で十分やることが2つ以上  →  if で書くelse がある        →  if で書く

短く書けるほうを選ぶけれど、読みにくくなったら if に戻すんだ。

08 / 08

さあ、打ってみよう

この回で使う形をまとめておくね。

if [ -f "$1" ]; then …; fi        有るならif [ -d "$1" ]; then …; fi        入れ物ならif [ -z "$1" ]; then …; fi        空ならif [ "$1" = "aka" ]; then …; fi   同じならif [ $# -eq 0 ]; then …; fi       個数が0ならif grep -q x "$1"; then …; fi     見つかったら

書くときは、こう足していくよ。

echo 'if [ -f "$1" ]; then' >> a.shecho '  wc -l < "$1"' >> a.shecho 'else' >> a.shecho '  echo nai' >> a.shecho 'fi' >> a.sh

1行ずつ >> で足すんだ。全部シングルで囲むのを忘れないでね。

書いたら cat で確かめるのも忘れずに。行が抜けていると fi が足りないと怒られるよ。

-sh: a.sh: line 5: syntax error: unexpected end of file

これが出たら、fi の書き忘れだね。閉じ忘れはいちばん多いまちがいだよ。

そして今日の結論を書いておくね。

> if はコマンドの成否で分かれている

[ もコマンド、grep もコマンド。どちらも同じように書けるでしょう。この仕組みが分かっていれば、暗記するものはほとんど無いんだ。では作ってみよう。