31.シェルスクリプトでプログラミング。Ctrl^Cで強制終了。
ナックス「よし!今回は遂にプログラミングの作成だ!」
デビー君「いぇい!」
ナックス「前回説明したように、今回はシェルスクリプトという種類のプログラミングを作成したいと思います。CUI環境を立ち上げてください」
デビー君「はーい。立ち上げました」
ナックス「皆さん、知っていたでしょうか?実はプログラミングというのはテキストエディタで作成できるのです」
デビー君「え!?そうなの?0と1だけで作成するんじゃないの?」
ナックス「『0と1だけでプログラミングを作成した人がいる』というのは、プログラマの世界でも『そりゃ、まぁ、はるか昔にはいたかもしれないけど……本当にいたの?』くらいの都市伝説です。というわけで、さっそくnanoエディタでプログラムを作成しましょう」
ナックス「前回言ったように、今回は数字の1から10を表示するという、ごくごく初歩的なプログラムを作成します。ファイル名は……そうですね。count.shとでもしておきましょうか」
デビー君「へー。シェルスクリプトの時はファイル名は最後に.shってつけるんだ」
ナックス「あ。なるほど。そこに気がつきましたか。そういえばファイル名の拡張子の話をしていませんでしたね」
デビー君「かくちょうし?」
ナックス「拡張子とは、ファイル名の.shとか、.txtとかの部分のことです。プログラムの中には拡張子を見てそのファイルが何のためのファイルなのかを識別するものがあります。特に、最近はどうか知りませんが、ちょっと前まではWindowsでその傾向が顕著でした。逆に、Linuxでは、拡張子と関係なくそのファイルが何であるかを認識する仕組みをとっています。説明すると長いし、私も詳しく無いので深くは突っ込みませんが。言ってしまえば、Linuxではファイル名に拡張子をつけなくても何も問題ありません」
デビー君「じゃあ、.shってつけなくてもいいってこと?」
ナックス「はい。ですが、それでも拡張子はつけるべきです。なぜなら拡張子が無いと、肝心のユーザーである私たち人間はファイル名を見てもそのファイルが何のファイルだか分かりません」
デビー君「あー。人間である僕達が何のファイルかを一目で判断するために、拡張子をつける習慣を持った方が良いってことだね」
ナックス「そうです。それが言いたかった」
ナックス「さて、拡張子の話は置いておいて、早速プログラムを作成します」
$ nano count.sh
ナックス「さて、中身に以下のように書いてください」
#!/bin/sh a=1 while [ $a -le 10 ] do echo $a a=`expr $a + 1` done
デビー君「うわ!なんだか難しそうだな……」
ナックス「そうですね。所々、前回習ったa=1やecho、exprなどのキーワードが見えますね」
ナックス「while [ $a -le 10 ]の箇所は、whileと[の間にスペース。[と$aの間にスペース。$aと-leの間にスペース。-leと10の間にスペース。10と]の間にスペース。というように、スペースの位置にも気をつけてください」
ナックス「ちなみに、-leのlはLの小文字です」
ナックス「さらに、expr $a + 1を囲っている`は、バッククォートです。間違えないように気をつけましょう」
ナックス「これがバッククォート(`)です。Shiftキーを押しながらこのキーを押せばバッククォートが記入できます」
ナックス「ちなみに、以下の様にシングルクォート(')やダブルクォート(")などの種類もあります。これらもShiftキーを押しながらこのキーを押すことで文字を入力できます」
ナックス「今回は、特に危ないプログラムでは無いので、書き間違えても大丈夫です。もし『難しすぎて書けない……』っていう方がいれば、最初はコピペでも構いません」
ナックス「書けたら、保存して終了してください」
Ctrlキーを押しながらOキー(保存操作)
名前を決めてEnter。今回の操作の通りであれば、既にcount.shという名前が記入されているのでそのままEnterキーを押せばよい。
Ctrlキーを押しながらXキー(nanoの終了)
ナックス「はい!ls -lコマンドでcount.shファイルが出来ているかどうか確認しましょう」
$ ls -l
デビー君「ありますね」
ナックス「じゃあ、とりあえずちゃちゃっとプログラムを実行してみましょう。プログラムを実行するには以下の様にします」
$ ./count.sh
デビー君「あれ?許可がありません、っていうエラーが出ちゃった」
ナックス「そうです。なぜなら、先ほどのls -lで見たように、実行権限が無いからです。というわけで、実行権限をつけてもう一度実行してみましょう」
$ chmod u+x count.sh
./count.sh
ナックス「実行結果はこうなります」
$ ./count.sh 1 2 3 4 5 6 7 8 9 10
デビー君「おお。1から10まで表示された」
ナックス「というわけでプログラムの解説だ!」
ナックス「まず、一般的にプログラムというは書かれている命令の上から順に下に向かって実行されていきます。人間が文書を読むのと同じ感じですね」
#!/bin/sh a=1 while [ $a -le 10 ] do echo $a a=`expr $a + 1` done
ナックス「の一行目。#!/bin/shですが。これはシェルスクリプトを書くとき書かなければならない決まりになっています」
デビー君「へー。書かなきゃダメなの?」
ナックス「書かなきゃダメです。次の行の a=1 ですが、これは前回説明したものと同じ考え方です」
デビー君「aという名前の入れ物に1を入れたんだね」
ナックス「そうです。名前をつけた入れ物のことは変数というのでした。つまり、キチンといえばaと言う変数に1を入れたということです」
ナックス「さて、その次がちょいと難しい。ちょっとこれを見てイメージをつかんでほしい 」
while [ 条件 ] do 動作 done
ナックス「これは、条件が正しいあいだは動作を繰り返す、というものです。今回の中身の条件を見てみると」
$a -le 10
ナックス「となっています」
数値評価演算子 | 意味 |
---|---|
a -eq b | aとbが同じ |
a -ne b | aとbが違う |
a -gt b | aがbより大きい |
a -lt b | aがbより小さい |
a -ge b | aがbより大きいか等しい |
a -le b | aがbより小さいか等しい |
ナックス「こんな感じの意味です。今回は -le なので、a が 10 よりも小さいか等しい、という条件が正しい間、doとdoneの間に書かれた動作を繰り返します」
デビー君「ふむふむ。で、今回の動作は」
echo $a a=`expr $a + 1`
デビー君「だね。echo $aは、aの中身を表示しているのは分かるけど、a=`expr $a + 1`っていうのはなんだ?」
ナックス「expr $a + 1の部分は前回説明した様に、ただの足し算です。今回は a の中身が1なので、1 + 1ということになりますね」
ナックス「ただし、今回はその結果の2を、再び a の変数に入れています」
デビー君「aを使った計算の結果を、またaに入れられるの?なんだか無茶苦茶な動作な気がするんだけど……」
ナックス「いえいえ、こういうやり方はプログラミングの世界では結構普通です。さて、ざっと説明してみます。今回はこんな感じですね」
a=`expr $a + 1`
ナックス「これは要するにこうです」
『=の左側(a)』=『=の右側(`expr $a + 1')』
ナックス「プログラミングの世界では、『=の右側』が先に実行され、その結果が『=の左側(多くは変数)』に入れられます。そして、それで式は終了です。一度計算し終わったものを、再度計算などしないのです。『あ、あれ?aに2が入ったからさっきの$a + 1は実は2 + 1で……』などと深く考え無いのです。プログラムは過去を振り返らない!未来に向かってのみ突き進むのだ!!」
デビー君「そ、そうなんだ……。」
ナックス「さて、今回、expr $a + 1の前後に`(バッククォート)がついています。exprを使った計算の結果を変数に入れたい場合は`でexprの式を囲む決まりになっているそうです」
デビー君「へー」
ナックス「変数aに2が入ったところでdoneになりました。プログラムは上から下に向かって実行されるので、普通はこのままdoneの下に向かっていくところですが……。while [ 条件 ] do 〜 done の場合は、動作を終えてdoneにたどり着くと、一度 while [ 条件 ]の『条件』に、現在の状態が合致しているか確認をします」
デビー君「ほうほう」
ナックス「aに1を足したところで、結果は2。まだ『aが10よりも小さいか等しい』という条件が正しいので、再度do〜doneに書かれた動作を行います」
デビー君「なるほど。今度はaの変数の中に2が入っているから」
echo $a
デビー君「で2を表示するのか」
ナックス「そうです。そして」
a=`expr $a + 1`
ナックス「で、今度は変数aの中身は3になる、と」
デビー君「なるほど!そしてまたdoneまで処理を実行した後に条件を確認して……そうやって1ずつ数が増えて、最後に11になると条件に当てはまらなくなって……」
ナックス「while文を抜け出して、それ以上何の命令も書いていないから、プログラムを終了する、ということです」
ナックス「さて!今回教えたかったのは実はこれだけじゃない!」
デビー君「え?何?」
ナックス「無限ループからの脱出!Ctrlキーを押しながらCキーを押せ!」
ナックス「以前、killコマンドでGUIプログラミングの強制終了の方法を教えました」
ナックス「実はCUI環境でも強制終了の方法があるのです」
ナックス「それが!Ctrlキーを押しながらCキーを押すこと!」
デビー君「へー。でも、CUI環境でフリーズすること何てあるの?」
ナックス「そうですね。メモリが異常に少ないパソコンならありえます。また、プログラミングを作っている途中に間違って無限ループするプログラムを作成して実行して、終了できない、っていうときに使います。他にも、何かのコマンドで『げ!実行はしてみたものの、終わりかたが分からん!』みたいな時とか」
デビー君「無限ループ、って何?」
ナックス「永遠に動作を繰り返すことです。例えば今回のプログラムでは」
#!/bin/sh a=1 while [ $a -le 10 ] do echo $a done
ナックス「のように、exprでの足し算をなくすと、aは永遠に1。永遠に条件が正しいままです。これを実行してしまうと、プログラムはもうどうにも止まらない!」
デビー君「あー。なるほど。永遠に1を表示し続けるね」
ナックス「今回は皆さんには擬似無限ループで、本当にCtrlキーを押しながらCキーでCUIの実行中のプログラムの強制終了が出来るかどうか試してほしいと思います」
デビー君「擬似無限ループ?」
ナックス「要するに、正確には無限ループじゃないけど、このプログラムは終了するのに時間がかかるよ、っていうプログラムのことです。さて、先ほどのプログラムの数字部分を大きい数にしましょう。えーっと、30000くらいにしましょうか」
#!/bin/sh a=1 while [ $a -le 30000 ] do echo $a a=`expr $a + 1` done
デビー君「おお。確かにこれは実行し終わるのに時間がかかりそう」
ナックス「先ほどのプログラムを書き換えて上書きしちゃうなり、新しく別のファイル名で作成しても良いので、上記のシェルスクリプトを作成して実行。実行中にCtrlキーを押しながらCキーを押して、プログラムが強制的に中断されるか試してください。なお、『うちのパソコンは性能が良すぎて、30000でもすぐに終了しちゃったよ』って人は、さらに大きな数字を入力して試してください」
ナックス「Ctrlキーを押しながらCキーを押して、プログラムが強制終了できることを確認し、余裕がある人は実際に無限ループのプログラムを作成してCtrlキーを押しながらCキーを押すことにもチャレンジしてみてください」
ナックス「次回!ハッカーアタックの初歩!(32.初めてのプログラミング補足/diffコマンドとTabキー)」
デビー君「え!?ハッキング?」