upd マニュアル

1. 概要

upd は、ソートされた2つのファイルのマッチング処理を行ないます。 暗記しなければならないことを減らすため、 UNIX の awk や c に近い設計になっています。

2. 構文

  upd [オプション] -f プログラムファイル  マスタファイル [トランザクションファイル]
	または、
  upd 'プログラム' [オプション] マスタファイル [トランザクションファイル]

トランザクションファイルが指定されない場合は、 標準入力がトランザクションファイルになります。

オプション

-Fc
文字「c」が入力レコードのフィールドセパレータになります。ただし、 「-Ft」は文字「t」でなく、TAB(タブ) 文字をフィールドセパレータにします。 セパレータは BEGIN セクションで指定することもできます。
-i.j[n]
「i」、「 j」は数値で、 それぞれマスタファイルとトランザクション(ジャーナル)ファイルの キーフィールドの位置をあらわします。 例えば、-1.2 なら、 マスタファイルの第1フィールドとトランザクションファイルの 第2フィールドをキーフィールドして、マッチング処理が行なわれます。 このオプションを指定しない場合は、-1.1 が指定されたものとみなされます。 実務上は、第1フィールドをキーフィールドにするのが普通です。 2つの入力ファィルは、これらのキーフィールドについて、 昇順にソートされていなければなりません。 ソートを前提にして、マッチングが成立します。文字「n」を指定すると、 キーフィールドは文字列の辞書順でなく、数値 numeric として比較されます。
-f プログラムファイル
プログラムファイル名を指定します。
-m
1対nのマッチングを行ないます。すなわち、 マスタひとつに対して複数のトランザクションをマッチングすることができます。 これは、UNIX の join コマンドと本質的に同じ動作で、 在庫ファイルの更新などで便利に使えることがあります。 このオプションが指定されなければ、1対1のマッチングになります。 プログラム中に「FLUSH」セクションがあると自動的にセットされます。

3. プログラム構造

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)の組み合せです。以下、 ステートメントで使用可能な要素について述べます。

4. 変数

変数(variable)には、数値あるいは文字列を記憶するための単純変数と、 1次元の配列が使えます。変数名は、アルファベットで始まらなければなりません。 2字目以降は、数字も使えます。 数値と文字列の区別はありません。数値として解釈できる文字列は数値、 数値として理解できない文字列は文字列としてあつかわれます。また、 加算や連結演算子を使って数値と文字列の変換を行なうことができます。 例えば、

  a + 0
は数値で
  a ""
は文字列になります。あいまいな内容を、数値や文字列に強制したいとき、 この手を使います。 何もない演算子は連結演算子です。 つまり、変数を並べて書くと、連結した文字列が得られます。

1次元配列は

  配列名[インデックス]
で表現します。インデックスは、任意の文字列です。数値の必要はありません。 この結果、連想配列としての機能が得られます。 例えば、
  a["数量"] += n
といった具合です。また、次のような手法で、疑似的な2次元配列が作れます。
  array["2 3"]

5. 組み込み変数

次の変数名は、組み込み変数として予約されています。

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,..	トランザクションの入力フィールド
レコードセパレータのデフォルト(初期値)は改行文字、 フィールドセパレータのデフォルトは空白文字です。 レコードセパレータとして空文字列("")を指定すると、 空白行がレコードセパレータとなり、いわゆるマルチラインレコードがあつかえます。

6. フィールド

また、特殊な変数として、フィールドがあります。 upd は、レコードを1つ入力する度に、 指定されたフィールドセパレータによりフィールド分割をおこない、 マスタファィルのレコードについては $1, $2, $3, .. 、 トランザクションファイルのレコードについては ?1, ?2, ?3, .. という変数にフィールドの内容をセットします。入力 したレコード全体を必要とする場合は、それぞれ $0, ?0 で参照することができます。 フィールドは、変数名を使って、例えば、

  i = 3
  print $i
のように間接的に参照することもできます。

7. 定数

定数には、数値定数と文字列があります。数値定数は、 すべて浮動小数点数として扱われます。例えば、次のとおりです。

  123
  3.1416
  5.2836e-3

文字列は、".." で囲んで引用します。例えば、次のとおりです。

  "The day after."
  ""
"" は中身のない空文字列をあらわします。

文字列のなかで、次のような表現が可能です。

  \n	改行文字
  \t	タブ文字
  \\	文字としての \
  \"	文字としての"
  \ddd	文字のオクタル(8進表現)、例えば、\007 はベル(BEL)コード

8. 演算子

演算子の内容は、次のとおりです。 正規表現のマッチ、アンマッチとフィールド演算子を別にして、C 言語と同じです。 上から下に進むにしたがって、優先度が上ります。

  = += -= *= /= %=	代入
  ||			論理OR
  &&			論理AND
  !			論理否定
  ~ !~			正規表現のマッチ、アンマッチ
  < <= > >= != ==		関係演算子
  空白			結合演算子
  + -			加算、減算
  * / %			乗算、除算、モード演算
  + -			単項プラス、マイナス
  ++ --			インクリメント、デクリメント
  $ ?			フィールド
マッチ、アンマッチの演算子は、if 文の条件として、次のような構文で使用します。
  if (変数名 ~ 正規表現)
	または
  if (変数名 !~ 正規表現)
これは、パターンのチェックに威力を発揮します。

9. 正規表現

正規表現は、文字列のパターンを認識するための有力な概念です。 これによって、複雑な文字パターンの指定を容易におこなうことができます。 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 にマッチ

10. 組み込み関数

次のような組み込み関数が用意されています。

10.1. 数値関数

cos(x)
三角関数、cos(x) を求めます。
exp(x)
指数関数、exp(x) を求めます。
int(x)
x の整数部を求めます。
log(x)
x の(自然)対数を求めます。
sin(x)
三角関数、sin(x) を求めます。
sqrt(x)
x の平方根を求めます。

10.2. 文字列関数

index(s,t)
文字列 s 中の、文字列 t の位置を求めます。 先頭は 1 になります。 存在しない場合は、0 が戻ります。
length(s)
文字列 s の長さを求めます。
split(s,a[,f])
フィールドセパレータ「f」によって、文字列「s」を分割し、 配列「a」にセットします。「f」を省略した場合は、 FS をフィールドセパレータとします。
sprintf(フォーマット, プリントリスト) .br プリントリストをフォーマットによって編集した文字列を返します。 フォーマットは printf 文と同じです。
substr(s, n[, m])
文字列「s」の「n」字目から「m」字長のサブストリングを作ります。 「m」を省略すると、「n」字目から最後までになります。

11. 制御文

if, while, for の 3 つが用意されています。

if (式) ステ―トメント1[ else ステ―トメント2]
式が成立すれば ステ―トメント1 、 成立しなければステ―トメント2を実行します。
while (式) ステ―トメント
式が成立する間、ステ―トメントを実行します。
for (式1; 式2; 式3) ステ―トメント
最初に式1を実行し、その後、式2の成立している間、 ステ―トメントと式3を実行します。
for (変数名 in 配列変数名) ステ―トメント
配列全体をアクセスするための構文です。指定された変数に、 指定された配列のすべての要素名を順次セットして、ステ―トメントを実行します。
break
for, while ループから抜け出します。
continue
for, while ループの先頭に戻ります。 式の値は、gpx の戻り値になります。
exit [式]
プログラムの実行を打ち切ります。

各ステートメント(命令)は、改行文字かセミコロン (;) で区切ります。 また、いくつかのステートメントを中括弧 { .. } でまとめて、複文(実効的に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) と呼ばれる技法を使用しているためです。

12. データ出力

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         |%|

13. ユーザ定義関数

ユーザ定義関数は下記の形式で記述します。

  function 関数名(引数リスト) {
    プログラム
    ..
    [return 式]
  }
関数の最後に return 文があれば、その式の値を持ち帰ります。 引数リストは変数か定数を「,」で区切ったものです。

関数呼出の構文は下記のとおりですが、 関数の引数が関数呼出の引数より大きい場合は、 残りの引数はローカル変数になります。 引数はすべて値渡しです。

  関数名(引数リスト)

14. データ入力

upd は(更新される)マスターファイルと (更新データである)トランザクションファイルを自動的に読み込みますから、 通常、データ入力を必要としませんが、

getline() 関数で、ディスクのファイルを読むことも可能で、 その場合は次の構文を使います。

  getline("ファイル名")
  getline [変数] < "ファイル名"
2つ目の構文で変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。

レコードセパレータは、RS になります。 ファイル名を省略した場合は、標準入力から読み込みます。この場合は、単に

  getline
と書いても構いません。 getline() 関数は、読み込みが出来た時 1 、 EOF (ファイルの終わり)により読み込みができないとき 0 を返します。

getline 関数の用途は、 BEGIN セクションなどで他のファイルを読みたくなるといった場合です。 その場合は getline 関数を使います。

14. コマンドからのデータ入力

下記の構文でコマンドの実行結果を入力することができます。

  "コマンド" | getline [変数]
変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。

Kouichi Hirabayashi, 1989