the opy book
Thu Oct 10 09:31:37 JST 2019 (modified: Fri Oct 11 09:07:35 JST 2019)
views: 1712, keywords: この記事は最終更新日が5年以上前のものです。
2. レコードとアクション
2.1 (ノーマル)アクション
アクションは、opyの中で最もPythonと似ている機能です。例えば、次の例は、seq
から読み込んだ数字に2をかけて出力するという単純なものです。
$ seq 3
1
2
3
$ seq 3 | opy '{print(F1*2)}'
2
4
6
引数の{print(F1*2)}
がアクションです。{ }
の中にPythonの文を書くと、文が毎行の入力に対して適用されます。F1
は毎行で読み込んだ数字が格納された変数ですが、これは後で説明します。
2.2 リストアクション
実は、上の例は
$ seq 3 | opy '[F1*2]'
2
4
6
と、もっと短く書けます。[ ]
はPythonのリストを表し、opy
は{...}
の代わりにリストを書いておくと、そのままリストの内容を出力します。この表記を、リストアクションと呼びます。また、リストアクションと区別したいときは、{...}
の表記を使うアクションをノーマルアクションと呼びます。
2.3 レコードとフィールド
2.3.1 変数F1, F2, ...
先ほどのF1
は、「(各行の)1列目」を表します。opy
は標準入力から行を読み込むと、空白を見つけて自動で分割し、F1, F2, ...
という変数に割り当てます。これは、AWKにおける$1, $2, ...
を真似たものです。「F
」は「フィールド」の頭文字をとったものです。また、opyやAWKでは、入力される毎行のデータを「レコード」と呼びます。
2個以上のフィールドを持つデータを扱ってみましょう。次のようなデータをseq
とxargs
で作ります。
$ seq 10 | xargs -n 5
1 2 3 4 5
6 7 8 9 10
この出力について、例えばF3
の数だけを2で割って出力したければ、
$ seq 10 | xargs -n 5 | opy '[F3/2]'
1.5
4.0
という表記になります。また、他のフィールドも出力したいのであれば、
$ seq 10 | xargs -n 5 | opy '[F1,F2,F3/2,F4,F5]'
1 2 1.5 4 5
6 7 4.0 9 10
というようにリストに他のフィールドを加えるとよいのですが、これは後でもっと短い書き方ができるようになります。
本項の例のように、標準入力から読み込まれたデータは、フィールドに分解されるときに自動的にint型あるいはfloat型に変換されます。例を示します。
$ echo 1 1.1 aaa | opy '[type(F1), type(F2), type(F3)]'
<class 'int'> <class 'float'> <class 'str'>
この仕様には欠点もあり、次のような型に関するバグの原因となります。
$ echo 1 1.1 aaa | opy '[F1+F3]'
(エラー)
上の例の場合、F1
を文字列型に直す必要があります。
$ echo 1 1.1 aaa | opy '[str(F1)+F3]'
1aaa
2.3.2 変数F0
F0
は、行全体の情報を持った変数です。
$ echo 1 1.1 aaa | opy '[F0]'
1 1.1 aaa
$ echo "1 1.1 aaa" | opy '[F0]'
1 1.1 aaa
これも、AWKの変数$0
を真似たものです。
F0
の型は常に文字列型です。次の例は、F0
とF1
の違いを表したものです。
$ echo 1 | opy '[F0*10, F1*10]'
1111111111 10
また、F0
、F1
は別の変数として作成されるので、F1
を変更しても、F0
は変更されません。
$ echo 1 | opy '{F1+=10;print(F0)}'
1
2.3.3 リストF
さらに、フィールドはF1, F2, ...
だけでなく、F
というリストにも記録されています。F[0]
にはF0
、F[1]
にはF1
、・・・と同じ値が記録されています。
F
は、各フィールドに対して繰り返し処理をするときに便利です。
### 入力の例 ###
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
### 全フィールドの足し合わせ ###
$ echo {1..10} | opy '[sum(F[1:])]'
55
### 奇数列だけ取り出す ###
$ echo {1..10} | opy '[*F[1::2]]'
1 3 5 7 9
ただし、値は同じでもオブジェクトとしては異なるものなので、どちらか一方を変更しても、その変更は他方に反映されません。例を示します。
$ echo 1 | opy '{F1=0;print(F1, F[1])}'
0 1
$ echo 1 | opy '{F[1]=0;print(F1, F[1])}'
1 0
同じオブジェクトへの参照ができないのでこういう仕様になっていますが、アイデア募集中です。