ほぼ同じ内容をn回もつログファイルから、最初の1回目だけを出力するPerlのワンライナー
タイトルの通り、ほぼ同じ内容をn回持つログファイルから最初の1回目を取得して出力するPerlのワンライナーを書きました。対象は200個近くあるログファイルになります。
ログファイルのイメージ
ログファイルはこんな感じです。Oracleのautotraceの結果です*1。このログ自体は、実行計画+Elapsed timeを複数回取得するのが目的です。ほぼ同じ内容としたのは、Elapsed timeは若干ですがズレるためです。
6 rows selected. Elapsed: 00:00:00.29 Execution Plan ---------------------------------------------------------- Plan hash value: 2988506077 .... ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 6 | 360 | 6 (17)| 00:00:01 | |* 1 | HASH JOIN | | 6 | 360 | 6 (17)| 00:00:01 | |* 2 | TABLE ACCESS FULL| EMPLOYEES| 6 | 204 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| JOBS | 19 | 494 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("E"."JOB_ID"="J"."JOB_ID") 2 - filter("E"."SALARY">12000) Note ----- - dynamic sampling used for this statement Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 10 consistent gets 0 physical reads 0 redo size 706 bytes sent via SQL*Net to client 496 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 6 rows processed
これがn回続きます。取得したいのは、最初の「6 rows selected.」から次の「6 rows selected.」までの間です。
6 rows selected. (省略) 6 rows selected. (省略) 6 rows selected. (省略) 6 rows selected. (省略) 6 rows selected. (省略)
使用したPerlのワンライナー
こんなのgrep+正規表現で一発や!と色々(10分程度)格闘した結果、うまくいかなかったのでPerlのワンライナーで解決しました*2。
$perl -ne '$l{$_}++; last if(/^(\d+|no) rows selected\.?$/ && $l{$_}==2); print $_;' hogehoge.log
あとは標準出力をファイルにリダイレクトしてあげて。
$perl -ne '$l{$_}++; last if(/^(\d+|no) rows selected\.?$/ && $l{$_}==2); print $_;' hogehoge.log > unique_hogehoge.log
ファイルには以下のように1回めだけが出力されます。
6 rows selected. (省略)
簡単な解説
perl -ne
-eオプションでPerlのワンライナーを利用可能にします。シングルコーテーション内がスクリプトになります。-nオプションで繰り返し=while相当です。つまり、$perl -neで「引数のファイルの内容で繰り返す」を表現しています。
$perl -ne 'print $_' hogehoge.log # hogehoge.logの内容をすべて出力
$l{$_}++;
ハッシュ$lにキー$_を設定して、インクリメントしています。$_は組み込み変数で宣言不要で使える変数です。今回の場合はwhileの引数にしているhogehoge.logの1行ずつが格納されています。hogehoge.logの内容が以下のように設定されているとして。
あああああ いいいいい ううううう あああああ あああああ いいいいい
出力回数のカウント+ファイルの内容を出力するワンライナーが簡単に作れたりします。Perlでユニークを作りたい時によく使うテクニックですね。。
perl -ne '$hash{$_}++; print "$hash{$_}: $_"' hogehoge.log
出力結果は以下のとおり。
1: あああああ 1: いいいいい 1: ううううう 2: あああああ 3: あああああ 2: いいいいい
last if(/^(\d+|no) rows selected\.?$/ && $l{$_}==2);
if内の処理が1行の場合は処理を先に書いて、処理の後にif文を書く構文が許容されます。本例の場合、ifに該当したらlast(whileを中断する)になります。if文の条件句では、「正規表現で狙った行にマッチする」かつ「ハッシュで2回目の登場」の場合としています。正規表現ブロックでは行が選択できた場合(\d+)と行が選択できない場合(no)を表現しています*3。これにより、最初の「6 rows selected.」から次の「6 rows selected.」まで間はwhile文を繰り返すを実現しています。
print $_
解説不要、単なるprintですね。