質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

Q&A

解決済

2回答

411閲覧

awkで指定した範囲に特定の文字列が含まれている場合のみ出力したい

eorou

総合スコア1

AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

0グッド

0クリップ

投稿2024/02/07 05:39

編集2024/02/07 05:42

特定の処理のログを抽出したく、awkで行っています。
範囲指定はできるのですが、条件を加えると最初の行が取得できません。

ログファイルは下記のような感じです。

start 2024/01/01 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:AAA NAME:AAA PASS:AAA DESCRIPTION:AAA 2024/01/01 00:00:10.123 XXX-XXXXX-XXX RunB [END] end start 2024/01/10 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:BBB NAME:BBB PASS:BBB DESCRIPTION:BBB 2024/01/10 00:00:10.123 XXX-XXXXX-XXX RunB [END] end start 2024/01/15 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:ABC NAME:ABC PASS:ABC DESCRIPTION:ABC 2024/01/15 00:00:10.123 XXX-XXXXX-XXX RunB [END] end start 2024/01/15 00:00:20.123 XXX-XXXXX-XXX RunA [START] ID:CCC NAME:CCC PASS:CCC DESCRIPTION:CCC 2024/01/15 00:00:30.123 XXX-XXXXX-XXX RunB [END] end

これに対し下記awkコマンド①を実行します。

awk '/RunA \[START\]/,/RunB \[END\]/' logfile

出力は下記で、これは期待通りです。

2024/01/01 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:AAA NAME:AAA PASS:AAA DESCRIPTION:AAA 2024/01/01 00:00:10.123 XXX-XXXXX-XXX RunB [END] 2024/01/10 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:BBB NAME:BBB PASS:BBB DESCRIPTION:BBB 2024/01/10 00:00:10.123 XXX-XXXXX-XXX RunB [END] 2024/01/15 00:00:00.123 XXX-XXXXX-XXX RunA [START] ID:ABC NAME:ABC PASS:ABC DESCRIPTION:ABC 2024/01/15 00:00:10.123 XXX-XXXXX-XXX RunB [END] 2024/01/15 00:00:20.123 XXX-XXXXX-XXX RunA [START] ID:CCC NAME:CCC PASS:CCC DESCRIPTION:CCC 2024/01/15 00:00:30.123 XXX-XXXXX-XXX RunB [END]

しかし、下記awkコマンド②の場合、期待通りにはなりません。

awk '/RunA \[START\]/,/RunB \[END\]/ {if ($0 ~ /ID:BBB/) {found=1} if (found) print} /RunB \[END\]/ {found=0}' logfile

出力は下記の通りで、RunA [START]を含む行が出力されていませんでした。

ID:BBB NAME:BBB PASS:BBB DESCRIPTION:BBB 2024/01/10 00:00:10.123 XXX-XXXXX-XXX RunB [END]

なぜ②ではRunA [START]の行が出力されなくなったのでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

melian

2024/02/07 06:21

found の初期値が 0 だからです。
eorou

2024/02/07 09:32

ありがとうございます。おっしゃる通り、foundが1になった後のログしかprintされませんね・・・。 期待にそぐわない実装になっておりました。もう一度処理を考えてみます。
guest

回答2

0

ベストアンサー

/RunA \[START\]/,/RunB \[END\]/をひと塊とみて、その塊の中に、ID:BBBがあれば、その塊全体を出力したいということですかね?

ID:BBBは、塊の2行目にあるので、その塊が欲しいと思っても、それが分かった時に、1行目はすでに捨ててますね。
保存しておかないと。

sh

1awk '/RunA \[START\]/,/RunB \[END\]/ {a=a "\n" $0} 2 /RunB \[END\]/ {if(a~/\nID:BBB\n/) print substr(a,2);a=""}'

範囲指定を使わないともうちょっとすっきりします。
これが普通でしょうか。割と簡明に読めます。
RunA行の直後にID:行がある前提なので、そうじゃなければ修正要。

sh

1awk '/RunA \[START\]/{a=$0} 2 /^ID:BBB$/{print a; found=1} 3 found{print} 4 /RunB \[END\]/{found=0}'

別解としては、RSを改行以外に設定して、上記記述での「塊」単位で処理するか。

sh

1awk 'BEGIN{RS="\nend\nstart\n|\nend\n|start\n"} 2 /\nID:BBB\n/'

これは一番awkらしい書き方かと思いますが、セパレーターの記述がちょっと直感的でないか。

投稿2024/02/07 09:55

編集2024/04/08 11:23
otn

総合スコア85766

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

eorou

2024/02/08 11:39

いくつもご提案いただきありがとうございます。 ご提案いただいた内容で無事解決いたしました。
guest

0

参考までに、想定している結果を出力するためには以下の様にします。

bash

1awk '/RunA \[START\]/{start=$0} /ID:BBB/{found=1} found{if(start){print start;start=""};print} /RunB \[END\]/{found=0}' logfile

ちなみに、Perl では入力ファイルの内容を全て読み込んで(-0777 オプション)、該当の範囲を抽出します。

bash

1perl -0777 -ne 'print $& while /.*?RunA \[START\]\nID:BBB(.|\n)*?RunB \[END\]\n/g' logfile

投稿2024/02/07 09:48

編集2024/02/07 09:52
melian

総合スコア20574

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

eorou

2024/02/08 11:41

ありがとうございます。 Perlだとわかりやすいですね。これを機にPerlも学んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問