upd は、ソートされた2つのファイルのマッチング処理を行ないます。 暗記しなければならないことを減らすため、 UNIX の awk や c に近い設計になっています。
upd [オプション] -f プログラムファイル マスタファイル [トランザクションファイル] または、 upd 'プログラム' [オプション] マスタファイル [トランザクションファイル]
トランザクションファイルが指定されない場合は、 標準入力がトランザクションファイルになります。
upd は、2つの前もってソートされたファイルを読んで キーフィールドの比較をおこない、その結果に対応したアクションを実行します。 プログラムは、次の形式で書きますが、どのセクションも省略可能です。
BEGIN { アクション } MATCH { アクション } MONLY { アクション } JONLY { アクション } FLUSH { アクション } END { アクション }BEGIN セクションは、プログラムの最初に一度だけ実行されます。その後、 マスタとトランザクションの2つのファイルから、 それぞれ1レコードづつ読み取ってキーフィールドを比較し、その結果に応じて、
MATCH - マスタとトランザクションのキーが一致 MONLY - マスタに対応するトランザクションがない JONLY - トランザクションに対応するマスタがないのうちいずれか1つの該当するアクションを実行するという処理を繰り返します。 そして、2つのファイルのすべてのレコードの処理が終ると、最後に、 END に対応するアクションを1度だけ実行して終了します。
MATCH に対応するアクションが実行されるのは、 マスタとトランザクションのキーが一致したときだけです。 マスタのキーがトランザクションのキーより小さいか、 トランザクションが EOF (End of File .. ファイルの終り)に達していて マスタのみ残っている場合は、MONLY のアクションが実行されます。 逆に、トランザクションのキーがマスタのキーより小さいか、 マスタが EOF に達していてトランザクションのみ残っている場合は、 JONLY のアクションが実行されることになります。
マスター1件分の処理が終わったとき FLUSH セクションが実行され、 これは1対nマッチングで更新済みマスタを書き出すときに使われるのが普通です。 BEGIN, MATCH, MONLY, JONLY, FLUSH, END のセクションはすべて省略可能です。
アクション(action)は、ステートメント(statement)の組み合せです。以下、 ステートメントで使用可能な要素について述べます。
変数(variable)には、数値あるいは文字列を記憶するための単純変数と、 1次元の配列が使えます。変数名は、アルファベットで始まらなければなりません。 2字目以降は、数字も使えます。 数値と文字列の区別はありません。数値として解釈できる文字列は数値、 数値として理解できない文字列は文字列としてあつかわれます。また、 加算や連結演算子を使って数値と文字列の変換を行なうことができます。 例えば、
a + 0は数値で
a ""は文字列になります。あいまいな内容を、数値や文字列に強制したいとき、 この手を使います。 何もない演算子は連結演算子です。 つまり、変数を並べて書くと、連結した文字列が得られます。
1次元配列は
配列名[インデックス]で表現します。インデックスは、任意の文字列です。数値の必要はありません。 この結果、連想配列としての機能が得られます。 例えば、
a["数量"] += nといった具合です。また、次のような手法で、疑似的な2次元配列が作れます。
array["2 3"]
次の変数名は、組み込み変数として予約されています。
FILENAME マスタファィルの名前 JFILENAME トランザクションファイルの名前 (標準入力のとき "-" ) RS マスタファイルのレコードセパレータ JRS トランザクションファイルのレコードセパレータ FS マスタのフィールドセパレータ JFS トランザクションのフィールドセパレータ NR マスタのレコードの連番 (1, 2, ..) JNR トランザクションのレコードの連番 (1, 2, ..) NF マスタレコードのフィールド数 JNF トランザクションレコードのフィールド数 OFMT print 文の出力フォーマット (ディフォルトは %.6g) ORS 出力レコードセパレータ OFS 出力フィールドセパレータ $0 マスタの入力レコード ?0 トランザクションの入力レコード $1,$2,$3,.. マスタの入力フィールド ?1,?2,?3,.. トランザクションの入力フィールドレコードセパレータのデフォルト(初期値)は改行文字、 フィールドセパレータのデフォルトは空白文字です。 レコードセパレータとして空文字列("")を指定すると、 空白行がレコードセパレータとなり、いわゆるマルチラインレコードがあつかえます。
また、特殊な変数として、フィールドがあります。 upd は、レコードを1つ入力する度に、 指定されたフィールドセパレータによりフィールド分割をおこない、 マスタファィルのレコードについては $1, $2, $3, .. 、 トランザクションファイルのレコードについては ?1, ?2, ?3, .. という変数にフィールドの内容をセットします。入力 したレコード全体を必要とする場合は、それぞれ $0, ?0 で参照することができます。 フィールドは、変数名を使って、例えば、
i = 3 print $iのように間接的に参照することもできます。
定数には、数値定数と文字列があります。数値定数は、 すべて浮動小数点数として扱われます。例えば、次のとおりです。
123 3.1416 5.2836e-3
文字列は、".." で囲んで引用します。例えば、次のとおりです。
"The day after." """" は中身のない空文字列をあらわします。
文字列のなかで、次のような表現が可能です。
\n 改行文字 \t タブ文字 \\ 文字としての \ \" 文字としての" \ddd 文字のオクタル(8進表現)、例えば、\007 はベル(BEL)コード
演算子の内容は、次のとおりです。 正規表現のマッチ、アンマッチとフィールド演算子を別にして、C 言語と同じです。 上から下に進むにしたがって、優先度が上ります。
= += -= *= /= %= 代入 || 論理OR && 論理AND ! 論理否定 ~ !~ 正規表現のマッチ、アンマッチ < <= > >= != == 関係演算子 空白 結合演算子 + - 加算、減算 * / % 乗算、除算、モード演算 + - 単項プラス、マイナス ++ -- インクリメント、デクリメント $ ? フィールドマッチ、アンマッチの演算子は、if 文の条件として、次のような構文で使用します。
if (変数名 ~ 正規表現) または if (変数名 !~ 正規表現)これは、パターンのチェックに威力を発揮します。
正規表現は、文字列のパターンを認識するための有力な概念です。 これによって、複雑な文字パターンの指定を容易におこなうことができます。 upd で使用可能な正規表現は次のとおりです。
c メタキャラクタ、すなわち . * + ? [ | ( ) \ 以外の c にマッチ \c リテラルキャラクタ c にマッチ \. 改行以外の任意の文字にマッチ ^ 行または文字列の先頭にマッチ $ 行または文字列の最後にマッチ [abc...] abc...のいずれかにマッチ [^abc...] abc...以外の文字にマッチ r1|r2 r1,r1のいずれかにマッチ r1r2 結合: r1 にマッチした後 r2 にマッチ r+ 1つ以上の r にマッチ r* 1個以上の r にマッチ r? 0または1個の r にマッチ (r) グルーピング: r にマッチ
次のような組み込み関数が用意されています。
if, while, for の 3 つが用意されています。
各ステートメント(命令)は、改行文字かセミコロン (;) で区切ります。 また、いくつかのステートメントを中括弧 { .. } でまとめて、複文(実効的に1つの文)にすることができ、これは while, for, then, else の後で必要になります。
例えば、次のプログラムは 1 から 10 までの合計を表示します。
s = 0; i = 1 while (1) { s += i if (++i > 10) break } print s
次のプログラムは、 先頭が文字 ! で始まらないマスタレコードのフィールドのみ表示します。
for (i = 1; i <= NF; i++) { if ($i ~ /^!/) continue print $0 }
次のプログラムはレコード中に「退職」の文字を含まないもののみ表示し、 そうでないものは件数をカウントします。
if ($0 !~ /退職/) print else cnt++
次のプログラムは 1 から 10 までの合計を表示します。
s = 0; i = 1 while (i < 11) s += i++ print s
次のプログラムも 1 から 10 までの合計をプリントします。
s = 0 for (i = 1; i < 11; i++) s += i print s
次のプログラムは配列 a のデータすべてを表示します。
for (i in a) print a[i]表示される要素の順番は不規則ですが、 これは配列の要素の検索にハッシング(hashing) と呼ばれる技法を使用しているためです。
upd の出力は print 文でおこないます。
print 式リスト[ >file] print 式リスト[ >>file] print 式リスト[ |\ command] printf フォーマット, 式リスト[ >file] printf フォーマット, 式リスト[ >>file] printf フォーマット, 式リスト[ | command]print 文はファイルにリダイレクト(>)したり、アペンド(追加)(>>)したり、 他のコマンドにパイプ入力(|)したりすることができます。
式リストは、プリントしたい式をコンマ(,)で区切って並べたもので、 コンマは出力レコードセパレータ OFS にかえて出力されます。 例えば、
a = 12; b = 3.14 print a, bなら
12 3.14と表示されることになります。 もちろん
OFS = ":" a = 12; b = 3.14 print a, bの出力は
12:3.14になります。 式リストの間にコンマがない場合は、すきまをあけずに出力されます。例えば、
a = 12; b = 3.14 print a bの出力は、
123.14となります。 数値の表現は、OFS で決まります。すなわち、 print 文は式リスト中の数値を OFS で指定されたフォーマットにもとづいて printf 文と同じ形式に編集し、その結果として得られる文字列を表示します。
printf 文のフォーマットは C 言語の print 文と同じで、 次の形式が用意されています。
%[-][印字幅][.最大文字列長または小数桁数]変換コード
変換コードの内容は、次のとおりです。
%c 1文字 %d 10進数 %e [-]d.ddddddE[+-]dd 形式 %f [-]ddd.dddddd 形式 %g e か f 形式のうち短いほう %o 符号無し8進数 %s 文字列 %x 符号無し16進数 %% % 自身を出力
具体例をいくつか示します。 %c で 97 を表示させると文字 a になるのは、 文字 a のASCII 値が 97 のためです。 ASCII 値というのは、 ASCII(アメリカの標準文字コード)の何番目の文字かという意味です。 %o と %x は、コンピュータ技術者が良く使います。 実用上は %s と %f で間に合いますので、まずこの2つを覚えてください。
fmt $1 printf(fmt, $1) ---------------------------------------- |%c| 97 |a| |%d| 97.5 |97| |%5d| 97.5 | 97| |%e| 97.5 |9.750000e+01| |%f| 97.5 |97.500000| |%7.2f| 97.5 | 97.50| |%g| 97.5 |97.5| |%.6g| 97.5 |97.5| |%o| 97 |141| |%06o| 97 |000141| |%x| 97 |61| |%s| January |January| |%10s| January | January| |%-10s| January |January | |%.3s| January |Jan| |%10.3s| Janualy | Jan| |%%| January |%|
ユーザ定義関数は下記の形式で記述します。
function 関数名(引数リスト) { プログラム .. [return 式] }関数の最後に return 文があれば、その式の値を持ち帰ります。 引数リストは変数か定数を「,」で区切ったものです。
関数呼出の構文は下記のとおりですが、 関数の引数が関数呼出の引数より大きい場合は、 残りの引数はローカル変数になります。 引数はすべて値渡しです。
関数名(引数リスト)
upd は(更新される)マスターファイルと (更新データである)トランザクションファイルを自動的に読み込みますから、 通常、データ入力を必要としませんが、
getline() 関数で、ディスクのファイルを読むことも可能で、 その場合は次の構文を使います。
getline("ファイル名") getline [変数] < "ファイル名"2つ目の構文で変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。
レコードセパレータは、RS になります。 ファイル名を省略した場合は、標準入力から読み込みます。この場合は、単に
getlineと書いても構いません。 getline() 関数は、読み込みが出来た時 1 、 EOF (ファイルの終わり)により読み込みができないとき 0 を返します。
getline 関数の用途は、 BEGIN セクションなどで他のファイルを読みたくなるといった場合です。 その場合は getline 関数を使います。
下記の構文でコマンドの実行結果を入力することができます。
"コマンド" | getline [変数]変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。
Kouichi Hirabayashi, 1989