サーバ管理者のためのプログラミング入門(シェルプログラミング基礎9~条件分岐その5) [サーバ管理者のプログラミング]
ここまでの解説で、コマンドの実行結果に応じて、あるいは変数の中身を判定した結果に応じて、またはファイルの特性を判定した結果に応じて処理を分岐する方法について理解してもらえたと思うが、ここまでの「条件分岐」は、シロかクロかという二者択一の処理分岐だった。
では、変数の中身に応じて複数の処理に分岐したいという場合はどうすればよいだろうか。
方法の一つとしては、if構文を羅列する方法がある。まずはこの点について説明を付け加えておく。
ここまでに説明したif構文の書式は、
if コマンドA then
真だった場合の処理
fi
または、
if コマンドA then
真だった場合の処理
else
偽だった場合の処理
fi
という形だった。だから、コマンドAが正常だったかエラーだったか(真だったか偽だったか)という分岐だけしか出来なかった。
ところが、時と場合によってはコマンドAの結果(具体的にはtestコマンド)で判定する変数の中身がシロかクロか…だけでなく、「1」だったら…「2」だったら…「3」だったら…という具合に処理を振り分けたいことも考えられる。
このような場合にこれまでのif構文を使った知識で克服するとなると、こう書けばよいことになる。
if test 変数 -eq 1 then
変数が1だったときの処理
fi
if test 変数 -eq 2 then
変数が2だったときの処理
fi
if test 変数 -eq 3 then
変数が3だったときの処理
fi
このように、if構文を羅列してしまう方法がまず考えられる。
しかしこの場合では変数が1だった場合、2以降の判定をすることが全くのムダになることが考えられる。変数が2だった場合の判定は、あくまでも「変数が1ではなかった」時にすればよいのではないか…と考えることもできる。
と、なると、このように記述を改めることもできる。
if test 変数 -eq 1 then
変数が1だったときの処理
else
if test 変数 -eq 2 then
変数が2だったときの処理
else
if test 変数 -eq 3 then
変数が3だったときの処理
fi
fi
fi
こうすれば、変数が1のときは「変数が1だったときの処理」を実行するが、2以降かどうかの判定は行わないし、変数が2のときは「変数が2だったときの処理」を実行するが3かどうかの判定は行わないことになり、処理の効率が向上する。
ただし、判断する件数が増えるとそれに伴ってif構文の「入れ子」が深くなってしまいスクリプトは見づらいものになってしまうということが容易に想像できると思う。
で、if構文にはこれに対処する方法がある。「else」句に「if」句を合体させた、「elif」という記述方法があるのであった。これを用いると、上記のif構文の固まりは次のように書き改めることが出来る。
if test 変数 -eq 1 then
変数が1だったときの処理
elif test 変数 -eq 2 then
変数が2だったときの処理
elif test 変数 -eq 3 then
変数が3だったときの処理
else
変数が1でも2でも3でもなかったときの処理
fi
このように、fiでif構文を閉じるのが1回で済むようになり、記述性がちょっぴり向上するというもの。最初のうちは感覚的に掴みにくいかもしれないけどね。
で、これよりももっと良い方法が有るのでそちらを紹介する。
単に変数の中身が1だったら…2だったら…3だったら…というようなケースで処理を複数パターン分岐させたい場合に有効なのが、「case構文」である。
サンプルをみてもらいたい。シェルスクリプトに仕立てて有る。
「case」にはじまり、「esac」に終わる固まりが、その「case構文」にあたる。変数「$1」(シェルスクリプトの位置パラメータ)の内容に応じて処理を分岐している。0なら「黒」、1なら「青」、2なら「赤」…で7だったら「白」と表示している。最後の「*」の部分は、0~7のどれにも当てはまらない場合に実行される部分になっている。
まずはこのスクリプトを実行してみよう。
シェルスクリプトのパラメータとして渡した数値に応じて表示される内容が変化していることが判る。
case構文の、in句の後ろに、パターンと処理の内容が記述されるが、小カッコ閉じる記号「)」の前(左側)に、パターンを記述する。このパターンとマッチすると、小カッコ閉じる記号「)」の後ろ側(右側)からの処理が実行され、末尾の「esac」句に到達するか、またはセミコロン2個「;;」の記述に到達するまでが実行される。パターンのマッチングは上から順に行われるため、どこか途中のパターンにマッチすると、それ以降のマッチングは一切行われない。
パターンの部分に「*」を記述すると、そこには全てのパターンがマッチすることになる。通常は個別のマッチングを行った後、一番最後に記述する。(理由は判りますか?)通常は、このブロックに「どのパターンにもマッチしなかった場合」の処理を書くのが通例である。そうした処理が必要ない場合はこのブロックを省略しても構わない。どれにもマッチしなかった場合はcase構文は何もせず構文を終了して次のコマンドを実行することになる。
ところで。たとえば、「変数の中身が1か3だったら○○の処理を、2か4だったら▲▲の処理を…」みたいなケースはどうするか。1と3とで同じことを、2と4とで同じことを記述しなければならないのか?という疑問のぶつかるかもしれない。
結論から行くとそんなことはない。
サンプルとして、1~12の「月」の値を指定すると、その月が何日有るかを教えてくれるスクリプトを紹介する。なお、面倒くさいしまだ説明していない部分が有るという諸般の事情にかんがみ、うるう年には非対応なのでそこんとこよろしく。(笑)
上記の例を見てもらうと判ると思うが、シェルスクリプトの引数に指定された値が、1か3か5か7か8か10か12だったら、「31日ある月です」と表示する処理を、4か6か9か11だったら「30日ある月です」と表示する処理を、2だったら「28日ある月です」と表示する処理をそれぞれ行っている。値は12種類あるが処理の内容は3種類だ。
マッチするパターンを「|」記号で区切って列挙することで、if構文で言うところのOR条件のようなものを設定することが可能となるのであった。
なお、このサンプルを実行すると…
こんな具合になる。
では、変数の中身に応じて複数の処理に分岐したいという場合はどうすればよいだろうか。
方法の一つとしては、if構文を羅列する方法がある。まずはこの点について説明を付け加えておく。
ここまでに説明したif構文の書式は、
if コマンドA then
真だった場合の処理
fi
または、
if コマンドA then
真だった場合の処理
else
偽だった場合の処理
fi
という形だった。だから、コマンドAが正常だったかエラーだったか(真だったか偽だったか)という分岐だけしか出来なかった。
ところが、時と場合によってはコマンドAの結果(具体的にはtestコマンド)で判定する変数の中身がシロかクロか…だけでなく、「1」だったら…「2」だったら…「3」だったら…という具合に処理を振り分けたいことも考えられる。
このような場合にこれまでのif構文を使った知識で克服するとなると、こう書けばよいことになる。
if test 変数 -eq 1 then
変数が1だったときの処理
fi
if test 変数 -eq 2 then
変数が2だったときの処理
fi
if test 変数 -eq 3 then
変数が3だったときの処理
fi
このように、if構文を羅列してしまう方法がまず考えられる。
しかしこの場合では変数が1だった場合、2以降の判定をすることが全くのムダになることが考えられる。変数が2だった場合の判定は、あくまでも「変数が1ではなかった」時にすればよいのではないか…と考えることもできる。
と、なると、このように記述を改めることもできる。
if test 変数 -eq 1 then
変数が1だったときの処理
else
if test 変数 -eq 2 then
変数が2だったときの処理
else
if test 変数 -eq 3 then
変数が3だったときの処理
fi
fi
fi
こうすれば、変数が1のときは「変数が1だったときの処理」を実行するが、2以降かどうかの判定は行わないし、変数が2のときは「変数が2だったときの処理」を実行するが3かどうかの判定は行わないことになり、処理の効率が向上する。
ただし、判断する件数が増えるとそれに伴ってif構文の「入れ子」が深くなってしまいスクリプトは見づらいものになってしまうということが容易に想像できると思う。
で、if構文にはこれに対処する方法がある。「else」句に「if」句を合体させた、「elif」という記述方法があるのであった。これを用いると、上記のif構文の固まりは次のように書き改めることが出来る。
if test 変数 -eq 1 then
変数が1だったときの処理
elif test 変数 -eq 2 then
変数が2だったときの処理
elif test 変数 -eq 3 then
変数が3だったときの処理
else
変数が1でも2でも3でもなかったときの処理
fi
このように、fiでif構文を閉じるのが1回で済むようになり、記述性がちょっぴり向上するというもの。最初のうちは感覚的に掴みにくいかもしれないけどね。
で、これよりももっと良い方法が有るのでそちらを紹介する。
単に変数の中身が1だったら…2だったら…3だったら…というようなケースで処理を複数パターン分岐させたい場合に有効なのが、「case構文」である。
サンプルをみてもらいたい。シェルスクリプトに仕立てて有る。
#!/bin/sh if [ -z "$1" ] then echo "色番号(0~8)を指定してください。" exit 1 fi case $1 in 0 ) echo "黒" ;; 1 ) echo "青" ;; 2 ) echo "赤" ;; 3 ) echo "紫" ;; 4 ) echo "緑" ;; 5 ) echo "水色" ;; 6 ) echo "黄色" ;; 7 ) echo "白" ;; * ) echo "色番号が正しい範囲ではありません" esac
「case」にはじまり、「esac」に終わる固まりが、その「case構文」にあたる。変数「$1」(シェルスクリプトの位置パラメータ)の内容に応じて処理を分岐している。0なら「黒」、1なら「青」、2なら「赤」…で7だったら「白」と表示している。最後の「*」の部分は、0~7のどれにも当てはまらない場合に実行される部分になっている。
まずはこのスクリプトを実行してみよう。
[root@kagami tmp]# ./degital_colors.sh 4 緑 [root@kagami tmp]# ./degital_colors.sh 6 黄色 [root@kagami tmp]# ./degital_colors.sh 7 白 [root@kagami tmp]# ./degital_colors.sh 9 色番号が正しい範囲ではありません
シェルスクリプトのパラメータとして渡した数値に応じて表示される内容が変化していることが判る。
case構文の、in句の後ろに、パターンと処理の内容が記述されるが、小カッコ閉じる記号「)」の前(左側)に、パターンを記述する。このパターンとマッチすると、小カッコ閉じる記号「)」の後ろ側(右側)からの処理が実行され、末尾の「esac」句に到達するか、またはセミコロン2個「;;」の記述に到達するまでが実行される。パターンのマッチングは上から順に行われるため、どこか途中のパターンにマッチすると、それ以降のマッチングは一切行われない。
パターンの部分に「*」を記述すると、そこには全てのパターンがマッチすることになる。通常は個別のマッチングを行った後、一番最後に記述する。(理由は判りますか?)通常は、このブロックに「どのパターンにもマッチしなかった場合」の処理を書くのが通例である。そうした処理が必要ない場合はこのブロックを省略しても構わない。どれにもマッチしなかった場合はcase構文は何もせず構文を終了して次のコマンドを実行することになる。
ところで。たとえば、「変数の中身が1か3だったら○○の処理を、2か4だったら▲▲の処理を…」みたいなケースはどうするか。1と3とで同じことを、2と4とで同じことを記述しなければならないのか?という疑問のぶつかるかもしれない。
結論から行くとそんなことはない。
サンプルとして、1~12の「月」の値を指定すると、その月が何日有るかを教えてくれるスクリプトを紹介する。なお、面倒くさいしまだ説明していない部分が有るという諸般の事情にかんがみ、うるう年には非対応なのでそこんとこよろしく。(笑)
#!/bin/sh if [ -z "$1" ] then echo "1~12の間の値を指定してください。" exit 1 fi case $1 in 1 | 3 | 5 | 7 | 8 | 10 | 12 ) echo "31日ある月です" ;; 4 | 6 | 9 | 11 ) echo "30日ある月です" ;; 2 ) echo "28日ある月です" ;; * ) echo "正しい範囲の引数ではありません。" esac
上記の例を見てもらうと判ると思うが、シェルスクリプトの引数に指定された値が、1か3か5か7か8か10か12だったら、「31日ある月です」と表示する処理を、4か6か9か11だったら「30日ある月です」と表示する処理を、2だったら「28日ある月です」と表示する処理をそれぞれ行っている。値は12種類あるが処理の内容は3種類だ。
マッチするパターンを「|」記号で区切って列挙することで、if構文で言うところのOR条件のようなものを設定することが可能となるのであった。
なお、このサンプルを実行すると…
[root@Welsper tmp]# ./nissu.sh 1 31日ある月です [root@Welsper tmp]# ./nissu.sh 3 31日ある月です [root@Welsper tmp]# ./nissu.sh 4 30日ある月です [root@Welsper tmp]# ./nissu.sh 2 28日ある月です [root@Welsper tmp]# ./nissu.sh 13 正しい範囲の引数ではありません。
こんな具合になる。
はじめまして。
サーバー管理の勉強を一からしようと思い検索していたら
このサイトに辿り着きました。解りやすく参考にさせてもらっています。
これからも、ちょくちょくお邪魔させていただきますのでよろしくお願いします
m(__)m
by yuu (2010-03-25 14:58)