基本コマンド編:レコード選択1(xtsel)

レコードの選択は非常に重要な処理で、MUSASHIでは多様な選択条件を記述することができる。
レコード選択の中心となるコマンドがここで解説するxtselコマンドである。


ここでは、先に作成したスクリプトxtcut.shを再利用することにする。xtsel.shという名前でコピーしておこう。

新しくコピーされたファイル"xtsel.sh"を編集する。
まずは、xtcutが出力するデータに対して、日付が2002年01月20日のデータだけを抜き出すことから始めよう。
xtselコマンドは -cパラメータで選択条件式を記述する。条件式の中で項目名の前には必ず"$"を付けるなければならない。
目的の条件式は次のようになる。'$日付==20020120'。ここで、"=="は数値比較演算子である。左右の値が同じであれば「真」を返し、同じでなければ「偽」を返す。xtselコマンドは条件式が真を返す行のみを選択する。その他の多様な比較演算子については後述する。
また注意点として、条件式には、様々な記号が含まれるので、通常は、条件式全体をシングルクオーテーションで囲む必要がある。そうしなければ、例えば$日付は、シェルが変数として解釈してしまうため、期待どおりの動作をしない。
さて、このxtselコマンドを追加したスクリプトは以下のようになる(そして最後に、出力ファイル名(xtsel.xt)、およびコメントの変更も忘れずに)。

#!/bin/bash
xtcut -f 日付,数量,金額 -i /mnt/h00/tutorial/dat.xt |
xtsel -c '$日付==20020120' |
xtheader -l "チュートリアル" -c "xtsel" -o xtsel.xt

ここで指定した三行の意味は次の通りである。
xtcutで「日付」、「数量」、「金額」の項目を選択し、その結果をパイプラインで次のxtselコマンドに送る。xtselコマンドでは、条件式「日付項目が20020120等しい」にマッチする行を選択する。そして、その結果データは再びパイプラインによって次のxtheaderコマンドに送られる。xtheaderコマンドは、タイトルとコメントを変更し、その結果を"xtsel.xt"というファイルに書き込む。

スクリプトの編集が終れば保存して、実行する。結果データを確認すると以下のように2002年01月20日のデータが選択されていることを確認できるであろう。

$ more xtsel.xt
<?xml version="1.0" encoding="euc-jp"?>
<xmltbl version="1.1">
<header>
<title>チュートリアル</title>
<comment>xtsel</comment>
<field no="1" name="日付"></field>
<field no="2" name="数量"></field>
<field no="3" name="金額"></field>
</header>
<body><![CDATA[
20020120 1 343
20020120 1 309
20020120 1 483
20020120 1 422
20020120 4 1204
20020120 1 111
20020120 1 247
20020120 4 2676
20020120 1 348
20020120 1 271
20020120 1 397
20020120 1 361
--More--(27%)

さて、ここでxtselコマンドが提供する基本的な比較演算子について見ておこう。下図はxtselで利用可能な基本的な比較演算子一覧である。

演算子 意味
== 左右の数値が等しい
< 左が右の数値より小さい
<= 左が右の数値以小
> 左が右の数値より大きい
>= 左が右の数値以上
<> 左右の数値が等しくない
-eq 左右の文字列が等しい
-gt 左が右の文字列より小さい
-ge 左が右の文字列以上
-lt 左が右の文字列より大きい
-le 左が右の文字列以下
-ne 左右の文字列が等しくない
|| 左右の条件式の論理和
&& 左右の条件式の論理積
=~ 正規表現によるパターンマッチ

これらの演算子を使い先のスクリプトにおけるxtselの条件式を変更してどのように動作するかを確認していこう。
まず最初に文字列比較についてである。XMLtableでは、項目の型が存在しない。それゆえに、項目の値をどのように扱うかは、条件式の書き方次第ということになる。例えば先に示した'$日付==20020120'という条件式での演算子"=="は左右の値を数値と見なして比較をする。これを文字列として比較したければ'$日付 -eq 20020120'ということになる。それでは先のスクリプトを文字列比較の式に変更して実行しよう。スクリプトは以下のようになる。実行結果は、前の結果と全く同じになるはずである。

#!/bin/bash
xtcut -f 日付,数量,金額 -i /mnt/h00/tutorial/dat.xt |
xtsel -c '$日付 -eq 20020120' |
xtheader -l "チュートリアル" -c "xtsel" -o xtsel.xt

次に、条件式を"&&"で連結する方法について見てみよう。例えば、「数量が5より大きく、かつ、金額が1000以上」の行を抜き出すためには、条件式として'$数量>5 && $金額>=1000'と指定する。スクリプトは以下のようになる。

#!/bin/bash
xtcut -f 日付,数量,金額 -i /mnt/h00/tutorial/dat.xt |
xtsel -c '$数量>5 && $金額>=1000' |
xtheader -l "チュートリアル" -c "xtsel" -o xtsel.xt

結果を確認すれば、条件どおりの行のみが選択されているのが確認できるであろう。
ここで重要なことは、演算子を複数用いる場合は、その優先順位を考慮に入れなければならない、ということである。例えば、先の例では、3つの比較演算子(>、&&、>=)を用いている。
>と>=演算子は&&演算子よりも優先される。また>と>=は優先順位に差がないので、出現順で評価される。例えば、条件式「$数量>5 && $金額>=1000」では、まず「$数量>5」が評価され、次に「$金額>=1000」が評価される。そして最後に「&&」の演算子が評価される。これらの優先順位を変更したければ括弧を用いればよい。
例えば、次のような条件式を考えて見よう。
「日付が20020115より大きいデータの中で、数量が5より大きい、もしくは、金額が1000以上」
この条件について、条件式'$日付>20020115 && $数量>5 || $金額>=1000'では、「日付が20020115より大きくかつ数量が5より大きいデータ、もしくは金額が1000以上のデータ」となってしまう。
そこで条件式に括弧を入れ、'$日付>20020115 && ($数量>5 || $金額>=1000)'とすればよい。スクリプトを変更して、それぞれの場合にどのようなデータが選択されるかを確認しよう。

さて最後に、算術演算子を含んだ比較式について紹介しておこう。xtselでは、比較演算子だけでなく、様々な演算子や関数を用いることができる。詳細は「項目間演算(xtcal)」を参照してもらいたい。
ここでは簡単に、割り算を用いた条件式について見てみよう。

これまでに用いてきたデータで金額とは商品単価×数量によって計算された値である。そこで、商品単価が100円以下の商品を選択する条件式を考えると次のようになる。
'($金額/$数量) <=100'。ここで"/"は割り算の演算子である。この式の意味は「金額を数量で割った値が100以下」である。
この式を反映させたスクリプトは下図のようになる。実行し結果を確認すれば、単価が100円以下の行が選択されているのがわかるであろう。

#!/bin/bash
xtcut -f 日付,数量,金額 -i /mnt/h00/tutorial/dat.xt |
xtsel -c '($金額/$数量)<=100' |
xtheader -l "チュートリアル" -c "xtsel" -o xtsel.xt

OnePoint 文字列比較と数値比較の違い
本章で見た日付項目に関する数値比較と文字列比較では、結果が全く同じになったが、数値比較と文字列比較は実は全く異なるものである。これは値の順序を考えれば明白である。例えば、"20"と"100"という値を数値として見た場合、当然100の方が大きい。しかし、文字列として見た場合は"20"の方が大きいと判断される。文字列の比較においては、文字列を先頭文字から順番に、その文字コードの大きさで比較していく。まず"20"の"2"と"100"の"1"を比較する。この時点で、文字コードでは"1"より"2"の方が大きい。よって"20"のほうが大きいと判断される。日付比較で同じになったのは、日付項目が固定長(8文字)であったからである。

練習課題

次のようなデータを作成しよう。スクリプト名及び結果ファイル名は表に示されたものを使おう。

帳票名 スクリプト名 結果ファイル(xt) 結果ファイル(html)
日付が20021120より大きく、かつ、金額が1000を超えるデータの日別数量金額合計 xtsel1.sh xtsel1.xt xtsel1.html
時間が170000(17時)以上で、かつ単価が100以下のデータの日別数量金額合計 xtsel2.sh xtsel2.xt xtsel2.html
メーカーが"0001"の日別数量金額合計 xtsel3.sh xtsel3.xt xtsel3.html
メーカーが"0001"もしくは"0002"の日別数量金額合計 xtsel4.sh xtsel4.xt xtsel4.html