Lesson 6 バッチ(シェルスクリプト)を学ぶ

    Lesson5まで終えた時点で、売上データから一つの帳票(時間別売上)が完成するに至った。そこではデータの解凍から始まって以下に示すように11回コマンドを実行することによって達成された。
 
bash $ gunzip -c /home/practice/Mine/Data/StoreMaster1/Dat/dat.gz >dat1-1 
bash $ mselstr field=2 string=19981010,19981011,19981012 <dat1-1-1 >dat2-1 
(OK) mselstr field=2 string=19981010,19981011,19981012 [In:107473 Out:1034 1998/04/09 17:52:34] 
bash $ mcut field=3,14,13 <dat2-1 >dat2-2 
(OK) mcut field=3,14,13 [In:1034 Out:1034 1998/04/09 17:58:42] 
bash $ sort <dat2-2 >dat1-3-1 
bash $ msum key=1 field=2,3 <dat3-1 >dat3-2
(OK) msum key=1 field=2,3 [In:1034 Out:12 1998/04/09 17:52:34] 
bash$ sort -n <dat3-2 >dat4-1 
bash$ echo '時間別売上数量金額' >xxtitle 
bash$ echo 'Lesson5にて作成' >>xxtitle 
bash$ date >>xxtitle 
bash$ echo ':時間: 数量: 金額:' >>xxfield 
bash$ mlist2 title=xxtitle field=xxfield <dat4-1 >dat5-1 

各コマンドはコマンドライン上から入力され実行してきた。しかしながらこの方法には次のような欠点がある。
    1)間違えて入力した場合、その修正のために再びコマンドを打ち直さなければならない。
    2)どのような処理をしたかに関する履歴が残らない。
一つ目の欠点に関しては、キー入力の補助(bashやtcshには実装されている)を使うことによって解決される。しかし2つ目の欠点は次のような場合、致命的となる場合が多い。
 a)帳票ができあがり、その帳票が印刷された。そして数日後、その帳票に誤りがあるのではないかと気付いた。しかし、どのように処理したか(どのようにコマンドを実行したか)に関する履歴がないために、それが誤りであるかどうか、確認することができない。
 b)時間別売上数量金額の帳票を仕上げた後で、日別の売上数量金額の帳票を作成したいと思った。もしコマンドの履歴があれば、最初のmcutのパラメータを変更するだけでよいにもかかわらず、最初からすべてを入力する必要が生じる。
 さて、これらの問題を解決するのがバッチ(もしくはシェルスクリプト)と呼ばれるものである。バッチ処理とは一括処理と訳される。すなわちここでは、コマンドをまとめて処理するという意味である。11個のコマンドをあらかじめテキストファイルとして用意し、その後に一括して実行する。また、そのファイルはbashやcshといったシェルが解釈し実行するという意味でシェルスクリプト(直訳すると「記述」)と呼ばれる。
 

1.バッチファイルの作成

    それでは実際にバッチを記述してみよう。そのためには、単にテキストエディタが利用できれば十分である。ここではUNIXの標準テキストエディタである"vi"を用いが、それぞれ利用しやすいテキストエディタを使えばよい。(viはキー操作が非常に煩雑で、初めてのユーザには非常に使いづらいエディタである。Windowsでは"notepad"や"wordpad"などのエディタを、Unixでは"mule"や"emacs"などのエディタを利用するととっつきやすい。)
    まず以下のように入力する。
 
bash$ jvim bat6-1
    "jvim"という文字の後に続く"bat6-1"はバッチファイル名である。上記のように入力すると、jvimの画面が表示され、自由に文字を入力することができるようになる。そこで、Lesson5までに打ち込んだ内容を以下のように記述しよう。これは単にワープロとして文字を入力しているだけなので、一行打ち込むたびにコマンドが実行されることはない。単に文書として入力しているだけである。

バッチファイルを書く時は1行目を空けること!

gunzip -c /home/practice/Mine/Data/StoreMaster1/Dat/dat.gz >xxa 
mselstr field=2 string=19981010,19981011,19981012 <xxa >xxb 
mcut field=3,14,13 <xxb >xxc
sort <xxc >xxd 
msum key=1 field=2,3 <xxd >xxe
sort -n <xxe >xxf
echo '時間別売上数量金額' >xxtitle 
echo 'Lesson5にて作成' >>xxtitle 
date >>xxtitle 
echo ':時間: 数量: 金額:' >>xxfield 
mlist2 title=xxtitle field=xxfield <xxf >xxtmp
    以上のように入力できれば保存し、その内容が正しいかどうか"less"コマンドなどで確認しておこう。内容が正しければ、いざ実行しよう。実行するためには以下のように入力する。
 
bash$ bash bat6-1
    これは、bat1-6-1に記述された内容を上から順番にbashに解釈させ実行させるという意味である。実行させると、以下のようなメッセージが表示され処理が終了する。
 
bash$ bash bat6-1
    このようにMコマンドの完了メッセージが順番に表示され(sortやechoコマンドはMコマンドでないので完了メッセージはない)、記述した11個のコマンドがすべて処理され終了する。できあがった帳票("dat5-1")の内容を確認してもらいたい。もし処理の途中でエラーメッセージが表示されたり、帳票の内容がおかしいようであれば、バッチの内容に誤りがあるためである。バッチの内容を確認し、再び実行してもらいたい。
 

2. より美しいバッチを作る

    前節にて作成したバッチは、非常に見にくい!!そこで見て美しいバッチに仕上げよう。見やすくするポイントは次の3点である。1)ほとんどのコマンドは入力ファイルと出力ファイルを伴っている。そこでそれらをきれいに列で揃える。2)タイトルや項目名などの作成は最初にもってくる。そうすることによって、バッチの最初をみれば何が書かれているかがわかる。3)Mコマンドは基本的には項目を番号で指定する。そこで番号と項目名の対応をコメントとして記述しておく。
    なお、このバッチを記述するときは、また最初から全てを記述するのではなく、コピーコマンドで前のバッチをコピーし、コピーした内容を修正すればよいであろう。
 
bash$ cp bat6-1 bat6-2
    新しく作成したバッチを以下に示しておく。
 
echo '時間別売上数量金額'  >xxtitle 
echo 'Lesson5にて作成' >>xxtitle 
date                                >>xxtitle 
echo ':時間: 数量: 金額:'    >>xxfield 

#入力ファイルの項目番号
#1:店コード,2:日付,3:時間,6:レシート番号,8:顧客コード,13:金額,14:数量,27:商品コード
gunzip -c /home/practice/Mine/Data/StoreMaster1/Dat/dat.gz     >xxa 
mselstr field=2 string=19981010,19981011,19981012          <xxa >xxb 
mcut field=3,14,13                                                           <xxb >xxc 
sort                                                                              <xxc >xxd 
msum key=1 field=2,3                                                      <xxd >xxe
sort -n                                                                          <xxe >xxf 
mlist2 title=xxtitle field=xxfield                                          <xxf >xxtmp

    ファイル名を揃え、タイトルを上にもっていき、項目番号の説明を加えた。このことによって以前のバッチ("dat1-6-1")に比べて、かなり見やすくなったであろう。他の分析者が見ても、その処理内容をある程度理解することができる。先頭文字が"#"で始まる行はコメントであり、なんら実行されない。また、ファイル名を揃えるためにはtabキーを使えば操作しやすい。
    なお、これは必要最低限の見易さであり、必要に応じて、作成者の名前や、その他の有用なコメントを入れておけばよいであろう。まったく分析をおこなった人には実感がないであろうが、分析を進め、多くのバッチファイルが作成されると、その管理が難しくなる。その時に、バッチの内容さえ見れば、その処理内容も理解できるようにしておくことは非常に重要なことである。

3. パイプで記述する

    最後にバッチをパイプを使って記述することについて解説しておこう。前節で作ったバッチ("bat6-2")は、コマンド毎にリダイレクトでファイル出力している。そのために次のような問題が生じる。1)全ての処理途中のファイルがディスク上に残るため、ディスクを浪費する。2)ファイルから入出力するために、処理時間がかかる。3)ファイル名の入力ミスが生じる。
    ただし最初の問題は、全てのケースにおいて悪いというわけではない。なぜならば、処理の途中経過を残しておけば、後でそのファイル内容を確認することによって誤りを発見できるし(「誤りを見つける」を参照)、さらに、その途中経過を一つのデータとして管理すれば、そのファイルを後から再利用できるからである(「中間ファイル」を参照)。
    さて、そこでこれらの問題を回避するためにパイプを利用してバッチを記述してみよう。ただし、パイプ処理を一行で記述すると以下に示すように非常に見にくくなる。
 
      :
      :
gunzip -c /home/practice/Mine/Data/StoreMaster1/Dat/dat.gz | mselstr field=2 string=19981010,19981011,19981012 | mcut field=3,14,13 | sort | msum key=1 field=2,3 | sort -n | mlist2 title=xxtitle field=xxfield >xxtmp 
そこで、一つのコマンドを一行に書くためにシェルの特殊文字である"\"を利用する。シェルは"\"の次の一文字は全く解釈しない。そのことを利用して以下のように記述する。
 
echo '時間別売上数量金額'  >xxtitle 
echo 'Lesson5にて作成' >>xxtitle 
date                                >>xxtitle 
echo ':時間: 数量: 金額:'    >>xxfield 

#入力ファイルの項目番号
#1:店コード,2:日付,3:時間,6:レシート番号,8:顧客コード,13:金額,14:数量,27:商品コード
gunzip -c /home/practice/Mine/Data/StoreMaster1/Dat/dat.gz | \
mselstr field=2 string=19981010,19981011,19981012                 | \
mcut field=3,14,13                                                                  | \
sort                                                                                      | \
msum key=1 field=2,3                                                              | \
sort -n                                                                                  >dat6-1

mlist2 title=xxtitle field=xxfield  <dat6-1 >dat6-2 
 

上記の"\"の次の文字は「改行文字」である。画面上には明示的に表示はされていないが、各行の最後には「改行文字」が存在する。シェルは一行を一つのコマンドと認識するために、もし"\"が無ければ、"|"を最後の文字として一行を実行する。しかしながらパイプ("|")に続くコマンドが記述されていないのでエラーとなる。そこで"\"を行の最後に記述することによって、次の一文字、すなわち「改行文字を」無視する。そのために、シェルはgunzipコマンドから始まる行の終わりは">dat6-1"として認識する(改行文字を5回無視している)。
    ちなみに、バッチの最後の方で、sortが終わった時点で"dat6-1"に書き出し、そのファイルからmlist2で帳票を作成している。特に
このようなことをする必然性はないが、「データ加工とデータの装飾は全く別の仕事」 という観点からこのようにしているに過ぎない。これはバッチを今後作成する上でのルールとして理解しておいて欲しい(バッチを作成のルールを参照)
    バッチを上記のように記述できたら実行してみよう。本節のはじめに指摘した問題はすべて解決されていることがわかるであろう。

・演習問題

a) Lesson5のa)の演習で作成したデータについて、パイプを用いたバッチで記述し実行しなさい。(回答はLesson5のa参照)
b) Lesson5のb)の演習で作成したデータについて、パイプを用いたバッチで記述し実行しなさい。(回答はLesson5のb参照)
c) Lesson5のc)の演習で作成したデータについて、パイプを用いたバッチで記述し実行しなさい。(回答はLesson5のc参照)
d) Lesson5のd)の演習で作成したデータについて、パイプを用いたバッチで記述し実行しなさい。(回答はLesson5のd参照)