• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法)

2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法

このQ&Aのポイント
  • 初心者の方でも2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法を教えてください。
  • AファイルとBファイルを比較し、結果をCファイルに抽出する方法を知りたいです。エクセルでは処理できないため、UNIXコマンドでの自動化が希望です。
  • Win2000のパソコンで2つの.logファイルを比較し、条件が満たしているレコードを抽出したいです。手作業で削除するのは大変なため、自動化できる方法を教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.11

> しかし、ひとつだけ今日トラブルがありました… > 私も考えていなかったので、質問には書かなかったのですが、 > ユーザIDと同じ名前が対象URLの中に紛れこむときもあるということです。 ああ、あえてその可能性には言及してなかったのですがそういうデータもありですか。 awkでやるなら話は簡単で、検索対象のフィールドを限定すればいいです。 $0 を対象にすると行全体からマッチするものを探していしまいますから、 期待するIDフィールドのところ(提示されたデータで判断すると$3)だけ 注目すればいいです。 ただこうすると、せっかくIDで探すときもIPアドレスで探すときも 共通のスクリプトが使えていたのに分離しなくてはならなくなるので、 何かの形で簡単なコマンドをAファイルの一行目に書けるとかしとくと いいかもしれません。スクリプトに対するコマンドラインオプションでも いいですけど。 NR==FNR { chkitems[$0]=1 next } ↑ここまでは共通で、 IPアドレスで検索するなら { for (idx in chkitems) { if (index($1, idx) > 0) print } } こうで、 ユーザーIDで探すなら { for (idx in chkitems) { if (index($3, idx) > 0) print } }

rundmark
質問者

補足

遅くなって申し訳ありません。 部分一致で検索すると、間違ったものをヒットしそうだったので (例:IPアドレスで10.114.2 と 10.114.29とか。) IPアドレスは NR == FNR { chktbl[$0] = 1 next } { if (match($0, /^[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?/)) { w = substr($0, RSTART, RLENGTH) if (w in chktbl) print } } でOKとし、 ユーザIDは { for (idx in chkitems) { if (index($3, idx) > 0) print } } にします。 ありがとうございました。

その他の回答 (10)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.10

> AファイルがIPアドレスとユーザIDと書きましたが、それぞれ別です。 > IPアドレスを調べたいときと、ユーザIDを調べたいときと別にしています。 > うまく説明ができていなくて申し訳ありません。 いや、わかってましたよ? どっちで探すかで別のスクリプト(それもほとんど変わらない) を用意するのはもったいないので、一つのスクリプトで どちらででも検索できるようにしただけです。 それと、 >if (match($0, /[A-Z][0-9][0-9][0-9][0-9][0-9][0-9][0-9/)) { これですけど、末尾の ']' 抜けは多分ポカミス でしょうからおいといて、 行の書式の先頭が > 2.222.2222.2222 - A11111 のようになっているのなら、awkはほっておいても 空白でフィールドへの分割をしてくれますから、 match で位置を確認して > w = substr($0, RSTART, RLENGTH) > if (w in chktbl) 切り出して云々とかやらないでも $3 in chktbl をチェックするだけでAファイルにあったIDか どうかを検査できます。 $1 ← 2.222.2222.2222 $2 ← - $3 ← A111111 のようになります。 それからgrepでうまくいかない原因ですが、 ひとつ気になるのがどんなgrepを使っているか ということです。 Unixのgrepを移植したものとか、その仕様をわかっている 人が作ったものでないやつだと(DOS/Windows用にはけっこうある) うまく動きません。

rundmark
質問者

補足

色々ありがとうございます。 大変感謝しております。 しかし、ひとつだけ今日トラブルがありました… 私も考えていなかったので、質問には書かなかったのですが、 ユーザIDと同じ名前が対象URLの中に紛れこむときもあるということです。 例: Aファイル N22222 Bファイル 0.000.000.000 - A11111 [01/Jan/2007:00:00:00 +0000] "GET testN22222.html HTTP/0.0" 000 000 とユーザIDは別ものであっても、対象URLに対象ユーザIDと同じ名前が入っていたら、検索としてひっかかってしまいます汗 ユーザIDの両端に空白が入っているので、 両端一行スペースをつけたり「\s」を追加したり色々試しましたが、私の知識ではとても分からないものでした。 この場合はどのような方法をとらえばいいでしょうか? ご教授よろしくお願いします。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.9

すんません。なんやかやで書けませんでした #別の質問に回答しているのに まだちょっと不明確なところがあるんですが (後述)、 こんな感じで。 NR==FNR { chkitems[$0]=1 next } { for (idx in chkitems) { pos = index($0, idx) if (pos > 0 && (pos==1 || substr($0, pos-1, 1)==" ")) { print break } } } 適当に作ったAファイルとBファイル 11.111.111 2.222.2222 33.33.333 443.444.4 B22222 E55555 2.222.2222.2222 - A11111 [01/Jan/2007:00:00:00 +0000] 3133 "GET test.html HTTP/0.0" 000 000 33.33.333.2334 - X01234 [02/Jan/2007:01:01:01 +0100] 4059 "GET test2.html HTTP/1.0" 111 - 33.33.333.9844 - B22222 [03/Jan/2007:02:02:02 +0200] 23875 "GET test3.html HTTP/2.0" 222 2222 443.444.3.4444 - C33333 [28/Aug/2007:03:03:03 +0300] 10358 "GET test4.html HTTP/3.0" 333 3333 123.123.123.222 - D44444 [29/Aug/2007:03:03:03 +0300] 14344 "GET index.html HTTP/3.0" 333 3333 234.56.789.1111 - E55555 [29/Aug/2007:03:03:03 +0300] 21374 "GET test4.html HTTP/3.0" 333 3333 254.254.254.2222 - F66666 [31/Aug/2007:03:03:03 +0300] 2437 "GET test4.html HTTP/3.0" 333 3333 結果: 2.222.2222.2222 - A11111 [01/Jan/2007:00:00:00 +0000] 3133 "GET test.html HTTP/0 .0" 000 000 33.33.333.2334 - X01234 [02/Jan/2007:01:01:01 +0100] 4059 "GET test2.html HTTP/1 .0" 111 - 33.33.333.9844 - B22222 [03/Jan/2007:02:02:02 +0200] 23875 "GET test3.html HTTP/ 2.0" 222 2222 234.56.789.1111 - E55555 [29/Aug/2007:03:03:03 +0300] 21374 "GET test4.html HTTP /3.0" 333 3333 サービス仕様で :) 複数種類のフィールドを一つのファイルに書けます んで質問。 #7の補足にあるデータ例だと、 > 3.33.333.233 - [02/Jan/2007:01:01:01 +0100] "GET test2.html HTTP/1.0" 111 - ユーザーIDがなかったり > 2.222.2222.222 - A11111 [01/Jan/2007:00:00:00 +0000] "GET test.html HTTP/0.0" 000 000 IPアドレスの三番目のフィールドが4桁あったりします。 これらのデータも正当なものですか? まあいずれにしろ、判定を甘い方向にしてますので 出るべきものが出ないということは多分ないと思います。 逆はあるかもしれませんが。

rundmark
質問者

補足

ああ、しまった。 説明不足でした… AファイルがIPアドレスとユーザIDと書きましたが、それぞれ別です。 IPアドレスを調べたいときと、ユーザIDを調べたいときと別にしています。 うまく説明ができていなくて申し訳ありません。 昨日、sakusaker7さんが書いてもらったスプリットを今日会社でやってみるとIPアドレスとしてはクリアしました。 NR == FNR { chktbl[$0] = 1 next } { if (match($0, /^[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?/)) { w = substr($0, RSTART, RLENGTH) if (w in chktbl) print } } 残るのユーザIDですが、以下のように正規表現を変えてみました。 NR == FNR { chktbl[$0] = 1 next } { if (match($0, /[A-Z][0-9][0-9][0-9][0-9][0-9][0-9][0-9/)) { w = substr($0, RSTART, RLENGTH) if (w in chktbl) print } } としたらうまくできました。 なんか結果オーライみたいで申し訳ありません。 もし、他にもっと簡略化とかUNIXコマンドでできそうだったらこれも教えてもらいたいと思っています。 grepがなぜできなかったのか今でも不思議です。今日試しましたがやはり無理でした。 しかし、スプリットは難しいものですね…汗 プログラムは奥深いものだと改めて実感させられました。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.8

仕事上のデータを出すことは要求しません。出したら危ないですし。 ただ、処理をする上で同じような性質をもったものを提示して欲しいのです。 たとえば一行は IPアドレス ユーザーID 時刻 で、ユーザーIDは アルファベット大文字一文字+数字4桁 時刻は24時間表記で 時:分:秒 というように。 もちろん、具体的なデータを提示した上で、それぞれの項目は これこれこういうものですと説明されるのがうれしいです。 説明する自分にとっては自明のことでも、こちらからみると明確に 説明されない限り推測するしかなくて、そこに齟齬が生じることがあります。 質問者さんはgrepでやったがうまくいかなかったといわれていますが、 質問で提示されたデータで試したところ私のところではうまく動いています(Tacosanさんにしても確認したうえで回答されているでしょう)。 おそらくいきなり本番のデータで試してそのようにお答えになったのでしょうが、 それは本番データと質問にあるデータの違いの何かが違っているのが 原因なわけです。最初に述べたように業務上のデータであるなら、 本物そのままを提示してもらう必要はありません。しかし、ダミーで あるならダミーの役割をこなせるデータでないと、回答者は動くつもりでいるのに、 質問者さんが業務用のデータで試したときにうまくいかないとなってしまうわけです。 >クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ というフォーマットなら、キーを収めたファイル(Aファイル)の 内容が正しければきちんと抜き出されるはずですし、 Aファイルの内容をユーザー名に変えれば、そのまま同じ手順で ユーザー名による抜き出しもできると思います。 #これはTacosanさんのgrepによる技の場合です #私のスクリプトではダメです ということで、パチもののデータでいいのでもうちょっと 「それらしい」データを例に出してください。

rundmark
質問者

補足

ありがとうございます。 そこまで頭がまわらなくて申し訳ありません。 うまく言葉の説明ができずsakusaker7さんの回答になるかどうかは分かりませんが。。。 Bファイルのレコードは クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ で間違いないです。 クライアントIPアドレス:Aファイルは3つ区切られていて、Bファイルは4つ区切られています。 ユーザーID:空白(-)と、アルファベット大文字一文字と数字7桁のどれかになっています。 私はアルファベット大文字一文字と数字7桁を対象に抜き出したいのです。 よろしくお願いします。 P.S. 会社から教えてグーは開くことはできませんが、携帯電話から見ることはできます。 もし、返事に必要なときは妻経由して掲載してもらうときがありますのでご了承をよろしくお願いします。 (それ以降は返事が夕方以降になります)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

え~, そもそも「B ファイルのどこにユーザID が入っているかわからない」んですけど.... 可能なら「本物の」A ファイル, B ファイルの中身 (の一部) を出してもらえないかなぁ. 「適当に書く」んじゃなく. 「grep でうまくいかない」理由も, 「本物の」ファイルが見えればわかるかもね.

rundmark
質問者

補足

本物だと仕事のプライベートに関わるのでお出しできませんので申し訳ございません。 本物に近く書くとしたら… Aファイル 11.111.111 2.222.2222 33.33.333 443.444.4 Bファイル 2.222.2222.222 - A11111 [01/Jan/2007:00:00:00 +0000] "GET test.html HTTP/0.0" 000 000 33.33.333.233 - [02/Jan/2007:01:01:01 +0100] "GET test2.html HTTP/1.0" 111 - 33.33.333.9844 - B22222 [03/Jan/2007:02:02:02 +0200] "GET test3.html HTTP/2.0" 222 2222 443.444.3 - C33333 [04/Jan/2007:03:03:03 +0300] "GET test4.html HTTP/3.0" 333 3333 これを Cファイル 2.222.2222.222 - A11111 [01/Jan/2007:00:00:00 +0000] "GET test.html HTTP/0.0" 000 000 33.33.333.233 - [02/Jan/2007:01:01:01 +0100] "GET test2.html HTTP/1.0" 111 - 33.33.333.9844 - B22222 [03/Jan/2007:02:02:02 +0200] "GET test3.html HTTP/2.0" 222 2222 のように作りたいと思っています。 よろしくお願いします。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.6

あーやっぱりIPアドレス(V4)でしたか。 でもそれだと最後が4桁あるのがわかりませんけど、 ゆるめに考えて 1~3桁の数字 . 1~3桁の数字 . 1~3桁の数字 だと考えるとこんな感じですか (この条件だと5桁~11桁になります) NR == FNR { chktbl[$0] = 1 next } { if (match($0, /^[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?/)) { w = substr($0, RSTART, RLENGTH) if (w in chktbl) print } } でも、このパターンなら Tacosan さん提案の grep を使う策でもいけそうな気がするんですが なぜうまくいかなかったんでしょう? 前回のとはちょっと変わってますが、使い方は同じです。 gawk -f スクリプト名 Aファイル Bファイル > Cファイル のようにしてください。 Cファイルの名前を固定して良いならその リダイレクトしなくても良いようにできます。 > print となっているところを print > "Cファイル" にしてください。

rundmark
質問者

補足

本当にありがとうございました。 今、手元に資料がないので明日会社で早速やってみます! grep -F -f Aファイル Bファイル > Cファイル ですよね? 何度もやりましたが空白でした…明日もう一度やってみます。 大変お世話になったところで申し訳ありませんが、もうひとつ今日会社で悩んだことがあります。 それはユーザIDだけ限定として調べたいときです。 AファイルはユーザIDだけで、BファイルはIPアドレスと調べたのと同じです。 本当に勉強不足で申し訳ありません。 ご知恵をお借りください。 よろしくお願いします。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

> 質問ですが、6~10桁までできるという風にするにはどうすればいいのでしょうか ? えーと情報がちと不足してます。 11.111.111.1111 22.222.222.1111 33.333.555.1111 44.444.444.1111 と質問に例示されていたので10桁決めうちにしたのですが、 > 2.2.2222 が8桁の例として、 6~10桁のデータがどういう形式なのか細くしてもらえますか? 単純に、 > 44.444.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200 これの、先頭の数字+ピリオドのデータのところから 最後のピリオドとその後ろの数字がないもの。 ということでいいのでしょうか? この辺が可変なデータだとちと面倒ですが、 データの書式の規則を明らかにしてもらえれば 対処できると思います。

rundmark
質問者

補足

申し訳ございません。 当方の説明不足です。 説明欄に記入したIPアドレスは10桁ですが、実際は何桁かどうか決まっていません。 (説明欄に記入したのは単に例でしたが、コマンドに影響を与えるとは思ってもいませんでした。) >6~10桁のデータがどういう形式なのか細くしてもらえますか? 6~10桁のデータですが、資料は会社にあり具体的なことは説明はできませんが、10桁と決まっていません。 私が8桁と追加した時点で全てのデータが正常に読み取れています。 今のところ。(月々のデータによって変動しますので、別の月では読み取れないことも考えられます。) >これの、先頭の数字+ピリオドのデータのところから >最後のピリオドとその後ろの数字がないもの。 >ということでいいのでしょうか? 例も適当に書いたものですが、正確に書くと クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ と書かれてあります。 ご迷惑をおかけしまして申し訳ありません。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.4

すみません使い方を説明してませんでした。 gawk -f スクリプト名 Aファイル Bファイル で実行してください。

rundmark
質問者

お礼

ありがとうございました。 最初はスクリプトの意味が分からず調べた点、 自分で作成して張り付けると理解しました。 そして、gawk -f スクリプト名 Aファイル Bファイル > Cファイル として実行したとき、うまくできました。 しかし、x = substr($1, 1, 10)は11.11.1111のように10桁のみで 2.2.2222のような8桁は対応できなかったので、 x = substr($1, 1, 8)と追加したことに解決しました。 質問ですが、6~10桁までできるという風にするにはどうすればいいのでしょうか ? fgrep -f Aファイル Bファイル (または grep -F -f Aファイル Bファイル) の件は、当パソコンにはfgrepのコマンドがインストールされていませんでした。 grep -F -f Aファイル Bファイルで試しましたが、うまくコマンドが動かずCファ イルは空白でした。。。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.3

ああ、条件が追加されなければfgrepでできますね。 Aファイルの内容を勘違いして、うごかねーとさんざ悩んでしまいました。 BEGIN { while ((getline < ARGV[1]) > 0) { chktbl[$0] = 1 } close(ARGV[1]) ARGV[1] = "" } { x = substr($1, 1, 10) if (x in chktbl) print } むりやりもっと短くできなくもないと思いますが 自粛 :)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

fgrep -f Aファイル Bファイル (または grep -F -f Aファイル Bファイル) でなんとかなるかも. 必要なら -n とかのオプションも付けるよ~に.

rundmark
質問者

お礼

ありがとうございました。 最初はスクリプトの意味が分からず調べた点、 自分で作成して張り付けると理解しました。 そして、gawk -f スクリプト名 Aファイル Bファイル > Cファイル として実行したとき、うまくできました。 しかし、x = substr($1, 1, 10)は11.11.1111のように10桁のみで 2.2.2222のような8桁は対応できなかったので、 x = substr($1, 1, 8)と追加したことに解決しました。 質問ですが、6~10桁までできるという風にするにはどうすればいいのでしょうか ? fgrep -f Aファイル Bファイル (または grep -F -f Aファイル Bファイル) の件は、当パソコンにはfgrepのコマンドがインストールされていませんでした。 grep -F -f Aファイル Bファイルで試しましたが、うまくコマンドが動かずCファ イルは空白でした。。。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.1

> UNIXコマンドでなんとか作業をしたいものです。 Windowsに何かその種のコマンドをインストールしてよい ということであれば、PerlでもRubyでも、awk(gawk)でも できると思いますが何か条件は他にありますか? それからBファイルが大きいというのはわかりましたが、 Aファイルはどのくらいでしょうか?

rundmark
質問者

お礼

ごめんなさい。文章を誤りました。 (補足の訂正ができないらしいので、回答へコメントを加えます) 誤:当方は初心者でawk(gawk)、sort、grep、wc、comm、catは使ったことがなく知りません。 ↓ 正:当方は初心者でawk(gawk)、sort、grep、wc、comm、catしか使ったことがなく他は使い方を知りません。

rundmark
質問者

補足

説明不足ですいません。 当方は初心者でawk(gawk)、sort、grep、wc、comm、catは使ったことがなく知りません。(ちなみにこのパソコンは会社のパソコンで、最初からインストールしていると思います。cygwinからインストールするのは存知じています。) Aファイルは軽い方です。 よろしくお願いします。

関連するQ&A