======================================================================= シェルスクリプトで yes or no ======================================================================= .. highlight:: bash :linenothreshold: 5 シェルスクリプトの中で対話的に処理をしたい ======================================================================= * シェルスクリプトを書いている時、実行している人に複数ある処理のうちの1つを選んでもらいたい、なんて事がよくありませんか? 例えば、 :: * yes だったら、あるファイルを削除 * no だったら、何もしない のような時、どのように書けば良いのでしょうか? **readコマンド** で対話的に変数を代入する ======================================================================= * 対話処理をするにはreadコマンドが一般的です。LinuxOS等にログインしシェルに打ち込んでみましょう。 :: $ read MAGICA ※ $はプロンプトの意味なので打たないでね! readコマンドを打ち込みEnterを押すと、プロンプトが返ってきません。ここで好きな文字列を入力します。例えば、 :: Mami と入力してみます。これで **MAGICA** という変数に *Mami* という文字列が代入されました。 本当に代入されたのかどうか確認してみましょう。変数名の頭に **$** を付けてechoコマンドで確認します。 :: $ echo $MAGICA ※ $はプロンプトの意味なので打たないでね! 次のように表示されれば成功です。 :: Mami 参照する変数によって処理内容を変更する **case文** ======================================================================= * シェルスクリプトに限らず、条件によって処理を分岐するには **if文** が有名かつ汎用性がありますが、今回はスマートな書き方として **case文** を使います。これもまたシェルに打ち込んでみましょう。 :: $ case $MAGICA in Mami) echo 'last name of Mami is Tomoe.' ;; *) echo 'MAGICA is none.' ;; esac ※ $はプロンプトの意味なので打たないでね! * 前のセクションから続けているならば「last name of Mami is Tomoe.」と表示されたはずです。 * 「 ) 」の左側に変数に代入される可能性のある文字列を書きます。上から順番に評価され、マッチした文字列があった場合は、その項目の処理を実行し終了します。次の項目には進みません。 * 一番最後に、どんな文字列にもマッチする 「 * 」 という項目を作っておくのが定石です。 readコマンドとcase文を組み合わせてスクリプトを書く ======================================================================= * では、実際に yesと打ち込むと"tyeped yes."、noと打ち込むと"tyeped no."と出力するようなスクリプトを書いてみましょう。 :: #!/bin/sh echo "Type yes or no." read answer case $answer in yes) echo -e "tyeped yes.\n" ;; no) echo -e "tyeped no.\n" ;; *) echo -e "cannot understand $answer.\n" ;; esac * answerという変数にyesかnoを代入させて、yes、no、それ以外の文字列が入力された場合で出力される文章がそれぞれ異なるスクリプトが出来上がりました! yesかnoの場合はいいけど、それ以外の時は? ======================================================================= * でもちょっと待って下さい。yesかno以外の文字列が入力された場合、yesかnoのどちらかが入力されるまで処理を繰り返したくありませんか? 繰り返し処理をさせたい時は、while文を使います。 :: #!/bin/sh while true;do echo "Type yes or no." read answer case $answer in yes) echo -e "tyeped yes.\n" break ;; no) echo -e "tyeped no.\n" break ;; *) echo -e "cannot understand $answer.\n" ;; esac done * **while true** と書くとwhile文の中を常にループ(繰り返し処理)させる事ができます。 * もちろん、永久ループするのは困るので、yesかnoを打った時の処理に **break** を書くことによりループを抜けられるようにします。 * yesでもnoでも無い時は、"cannot understand 「打ち込んだ文字列」"と表示させ、while文の頭に戻って処理が繰り返されます。 関数化する ======================================================================= * 実用的に使う為には関数化します。 :: #!/bin/sh yes_or_no_while(){ while true;do echo echo "Type yes or no." read answer case $answer in yes) echo -e "tyeped yes.\n" return 0 ;; no) echo -e "tyeped no.\n" return 1 ;; *) echo -e "cannot understand $answer.\n" ;; esac done } yes_or_no_while * シェルスクリプトでは関数を作り、コマンドのように実行する事ができます。今回は *yes_or_no_while* という関数を作り実行しています。 * 前のセクションでは **while文** を抜けるのに **break** を使っていましたが、関数化した際には **return** を使い関数を終了させています。関数化している場合は **break** より関数の終了値を返せる **return** がお勧めです。 ちなみに今回は終了値をyesの場合は0、noの場合は1と返していますが、このスクリプトだけでは特に意味がありません。 再帰呼出し ======================================================================= * さて無事にyesとno以外の文字列が入力された場合は、繰り返しyesかnoを聞くスクリプトが出来上がりました。が、while文はネスト(入れ子構造)になってしまい若干見辛いかなあ、という時があります。 * そう、もっとスマートに書く方法があります。関数の中で、同じ関数を呼び出す **再帰呼出し** という方法です。 :: #!/bin/sh yes_or_no_recursive(){ echo echo "Type yes or no." read answer case $answer in yes) echo -e "tyeped yes.\n" return 0 ;; no) echo -e "tyeped no.\n" return 1 ;; *) echo -e "cannot understand $answer.\n" yes_or_no_recursive ;; esac } yes_or_no_recursive * **case文** の一番最後の項目に注意して下さい。 yes、no以外の文字列の場合「 * 」は"cannot understand $answer."と出力し、18行目で *yes_or_no_recursive* という自分自身を呼び出しています。これでyes、no以外の時は関数の初めから繰り返し実行されます。ネストが減ってなんとなくスマートになった気がしませんか? bashで書いてみよう ======================================================================= * 上で書いていたスクリプトは汎用性を高くする為に、sh(Bourne Shell)で書いていましたが、bashだともう少しかっこいい書き方ができます。 :: #!/bin/bash function yes_or_no_select(){ PS3="Answer? " while true;do echo "Type 1 or 2." select answer in yes no;do case $answer in yes) echo -e "tyeped yes.\n" return 0 ;; no) echo -e "tyeped no.\n" return 1 ;; *) echo -e "cannot understand your answer.\n" ;; esac done done } yes_or_no_select * スクリプトを見るだけではわかりませんが **select文** を使うと表示がかっこよくなります。 * bashでは関数を書くときに **function** と頭に明示する事ができます。つけなくても関数は書けますが明示したほうがスクリプトが見易くなります。 まとめ ======================================================================= * **readコマンド** で対話的に変数を代入する * **case文** で分岐処理 * **while文** でループ(繰り返し処理) * **再帰呼出し** でスマートにループ