アプリ版:「スタンプのみでお礼する」機能のリリースについて

初心者です。

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



Aファイル

11.111.111
22.222.222
33.333.333
44.444.444


Bファイル

11.111.111.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.333.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.222.222.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.355.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.555.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
44.444.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200




Cファイル
11.111.111.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.222.222.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
44.444.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200


このように、AファイルとBファイルを比較し、結果をCファイルに抽出したいです。
エクセルでVlookupの関数を使う方法もありますが、Bファイルは容量が重いのでエクセルデータでは全部読み
取ることができません。
よって、UNIXコマンドでなんとか作業をしたいものです。
commを使う作業もありますが、手作業で不要な部分を削除していくのも大変なのでできれば自動化が希望です。

パソコンのOSはWin2000です。

どなたかご知恵をお借りください。
よろしくお願いします。

A 回答 (11件中1~10件)

> しかし、ひとつだけ今日トラブルがありました…


> 私も考えていなかったので、質問には書かなかったのですが、
> ユーザ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
}
}

この回答への補足

遅くなって申し訳ありません。

部分一致で検索すると、間違ったものをヒットしそうだったので
(例: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
}
}

にします。
ありがとうございました。

補足日時:2007/09/02 10:40
    • good
    • 0

> 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用にはけっこうある)
うまく動きません。

この回答への補足

色々ありがとうございます。
大変感謝しております。

しかし、ひとつだけ今日トラブルがありました…
私も考えていなかったので、質問には書かなかったのですが、
ユーザ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」を追加したり色々試しましたが、私の知識ではとても分からないものでした。

この場合はどのような方法をとらえばいいでしょうか?
ご教授よろしくお願いします。

補足日時:2007/08/30 19:17
    • good
    • 0

すんません。

なんやかやで書けませんでした
#別の質問に回答しているのに

まだちょっと不明確なところがあるんですが
(後述)、
こんな感じで。

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桁あったりします。

これらのデータも正当なものですか?

まあいずれにしろ、判定を甘い方向にしてますので
出るべきものが出ないということは多分ないと思います。
逆はあるかもしれませんが。

この回答への補足

ああ、しまった。
説明不足でした…

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がなぜできなかったのか今でも不思議です。今日試しましたがやはり無理でした。

しかし、スプリットは難しいものですね…汗
プログラムは奥深いものだと改めて実感させられました。

補足日時:2007/08/29 18:36
    • good
    • 0

仕事上のデータを出すことは要求しません。

出したら危ないですし。
ただ、処理をする上で同じような性質をもったものを提示して欲しいのです。
たとえば一行は

IPアドレス ユーザーID 時刻

で、ユーザーIDは アルファベット大文字一文字+数字4桁
時刻は24時間表記で 時:分:秒 というように。
もちろん、具体的なデータを提示した上で、それぞれの項目は
これこれこういうものですと説明されるのがうれしいです。

説明する自分にとっては自明のことでも、こちらからみると明確に
説明されない限り推測するしかなくて、そこに齟齬が生じることがあります。
質問者さんはgrepでやったがうまくいかなかったといわれていますが、
質問で提示されたデータで試したところ私のところではうまく動いています(Tacosanさんにしても確認したうえで回答されているでしょう)。

おそらくいきなり本番のデータで試してそのようにお答えになったのでしょうが、
それは本番データと質問にあるデータの違いの何かが違っているのが
原因なわけです。最初に述べたように業務上のデータであるなら、
本物そのままを提示してもらう必要はありません。しかし、ダミーで
あるならダミーの役割をこなせるデータでないと、回答者は動くつもりでいるのに、
質問者さんが業務用のデータで試したときにうまくいかないとなってしまうわけです。

>クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ
というフォーマットなら、キーを収めたファイル(Aファイル)の
内容が正しければきちんと抜き出されるはずですし、
Aファイルの内容をユーザー名に変えれば、そのまま同じ手順で
ユーザー名による抜き出しもできると思います。
#これはTacosanさんのgrepによる技の場合です
#私のスクリプトではダメです

ということで、パチもののデータでいいのでもうちょっと
「それらしい」データを例に出してください。

この回答への補足

ありがとうございます。
そこまで頭がまわらなくて申し訳ありません。

うまく言葉の説明ができずsakusaker7さんの回答になるかどうかは分かりませんが。。。
Bファイルのレコードは
クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ
で間違いないです。

クライアントIPアドレス:Aファイルは3つ区切られていて、Bファイルは4つ区切られています。
ユーザーID:空白(-)と、アルファベット大文字一文字と数字7桁のどれかになっています。
私はアルファベット大文字一文字と数字7桁を対象に抜き出したいのです。


よろしくお願いします。
P.S.
会社から教えてグーは開くことはできませんが、携帯電話から見ることはできます。
もし、返事に必要なときは妻経由して掲載してもらうときがありますのでご了承をよろしくお願いします。
(それ以降は返事が夕方以降になります)

補足日時:2007/08/29 07:47
    • good
    • 0

え~, そもそも「B ファイルのどこにユーザID が入っているかわからない」んですけど....


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

この回答への補足

本物だと仕事のプライベートに関わるのでお出しできませんので申し訳ございません。

本物に近く書くとしたら…

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

のように作りたいと思っています。
よろしくお願いします。

補足日時:2007/08/28 21:54
    • good
    • 0

あーやっぱり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ファイル"

にしてください。

この回答への補足

本当にありがとうございました。
今、手元に資料がないので明日会社で早速やってみます!

grep -F -f Aファイル Bファイル > Cファイル
ですよね?
何度もやりましたが空白でした…明日もう一度やってみます。

大変お世話になったところで申し訳ありませんが、もうひとつ今日会社で悩んだことがあります。
それはユーザIDだけ限定として調べたいときです。

AファイルはユーザIDだけで、BファイルはIPアドレスと調べたのと同じです。

本当に勉強不足で申し訳ありません。
ご知恵をお借りください。

よろしくお願いします。

補足日時:2007/08/28 20:56
    • good
    • 0

> 質問ですが、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

これの、先頭の数字+ピリオドのデータのところから
最後のピリオドとその後ろの数字がないもの。
ということでいいのでしょうか?

この辺が可変なデータだとちと面倒ですが、
データの書式の規則を明らかにしてもらえれば
対処できると思います。

この回答への補足

申し訳ございません。
当方の説明不足です。
説明欄に記入したIPアドレスは10桁ですが、実際は何桁かどうか決まっていません。
(説明欄に記入したのは単に例でしたが、コマンドに影響を与えるとは思ってもいませんでした。)

>6~10桁のデータがどういう形式なのか細くしてもらえますか?

6~10桁のデータですが、資料は会社にあり具体的なことは説明はできませんが、10桁と決まっていません。

私が8桁と追加した時点で全てのデータが正常に読み取れています。
今のところ。(月々のデータによって変動しますので、別の月では読み取れないことも考えられます。)

>これの、先頭の数字+ピリオドのデータのところから
>最後のピリオドとその後ろの数字がないもの。
>ということでいいのでしょうか?

例も適当に書いたものですが、正確に書くと
クライアントIPアドレス ユーザ名 接続日時 送信バイト サービス状態 操作内容 対象URL パラメータ
と書かれてあります。

ご迷惑をおかけしまして申し訳ありません。

補足日時:2007/08/28 18:50
    • good
    • 0

すみません使い方を説明してませんでした。



gawk -f スクリプト名 Aファイル Bファイル

で実行してください。
    • good
    • 0
この回答へのお礼

ありがとうございました。

最初はスクリプトの意味が分からず調べた点、
自分で作成して張り付けると理解しました。
そして、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ファ
イルは空白でした。。。

お礼日時:2007/08/28 14:39

ああ、条件が追加されなければ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
}

むりやりもっと短くできなくもないと思いますが
自粛 :)
    • good
    • 0

fgrep -f Aファイル Bファイル


(または grep -F -f Aファイル Bファイル)
でなんとかなるかも. 必要なら -n とかのオプションも付けるよ~に.
    • good
    • 0
この回答へのお礼

ありがとうございました。

最初はスクリプトの意味が分からず調べた点、
自分で作成して張り付けると理解しました。
そして、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ファ
イルは空白でした。。。

お礼日時:2007/08/28 14:39

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!