このようなテキストファイルがあります。
2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 destination
2014-11-24 13:59:37.113 23.234.22.106 source
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination
特定のIPアドレスが送信元と宛先として表示されている場合は、そのIPを次に表示したいと思います。両方。この例では、IP 23.234.22.106 は送信元と宛先です。だから私はそれを次のように表示したいと思います。両方。
私が望む出力は次のようになります
2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 both
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination
私が試したことは次のとおりです。
cat input.txt | awk '{print $3}' | sort | uniq | while read line
do
grep $line input.txt | sort -r -k1 | head -1
done
ただし、特定のIPを次のように表示する方法を理解していません。両方ソースなら目的地でもあります。この場合は23.234.22.106です。
awkを使ってこれを行うにはどうすればよいですか?これにご協力いただきありがとうございます。ありがとう
答え1
試してみてくださいsed
sed '
N #add next line
s/\([0-9.]\+\)\s\S\+\n.*\s\1\s\S\+$/\1 both/
P #print first line from two
D #remove first line, return to start
' input.txt
[0-9.]\+
数字と点のセット\s
スペースまたはタブ\S\+
空白以外の文字セット\n
新しいチーム.*
すべてのシンボル\1
角かっこで囲まれたグループの逆参照\(...\)
$
パターンの終わり
(修正:削除t
コマンド(tnx 2ゼチル)グローバルアドレスを確認するには、グループの前に\ spaceを追加してください)
答え2
そしてperl
:
#! /usr/bin/perl
use strict;
my @lines=();
while(<>) {
chomp;
s/#.*//g; # strip comments
s/^\s*|\s*$//g; # strip leading and trailing spaces
next if (/^$/); # skip blank lines
if (! scalar @lines) {
# store the first line of the file in the array
# we can't do anything else yet, so skip to the next line.
push @lines, $_;
next;
} else {
push @lines, $_;
# split both lines into space-separated fields.
my @a = split /\s+/, $lines[0];
my @b = split /\s+/, $lines[1];
# if 3rd fields are the same, change to "both"
if ($a[2] eq $b[2]) {
@lines = map { $_ =~ s/(source|destination)$/both/oi; $_} @lines;
}
}
print $lines[0],"\n";
shift @lines;
}
print $lines[0],"\n";
ここでのアイデアは、配列(@lines
)を使用して現在の行と前の行を保持することです。 2行の3番目のフィールド(0から始まるPerl配列)が同じ場合は、文字列「source」または「target」を「both」に変更します。
変更の有無にかかわらず、前の行を印刷します。次に、配列から前の行を削除し、次にshift
繰り返すときに現在の行が前の行になるようにします。
最後に、ループが完了した後、最後の入力行が印刷されます。
出力:
$ ./swatesh.pl <swatesh.txt
2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 both
2014-11-24 13:59:37.113 23.234.22.106 both
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination
いくつかの注意:
このsed
スクリプトはうまく機能します。しかし、なぜこのスクリプトを使用するのですかperl
?違いは何ですか?
@Costasのsed
バージョンは速いので、処理する行が数百万個ある場合は重要です。
このperl
バージョンは、2行のフィールド3が正確に同じであることを明示的に確認しますが、このバージョンsed
はIPアドレスに似ているパターンが後で同じ接続の2行で繰り返されますが、チェックします(必ずしも問題ではありません)。あなたの例を入力すると、このsed
バージョンはあなたの例に完全に最適化されています)。
このperl
バージョンは、さまざまな入力に適応するのが簡単です。
#
ループの先頭のコードは、空白行をスキップし、テキストファイルのコメントをサポートするために多くのPerlスクリプトで使用される便利なコードです。私はしばしばスクリプトで同じことをしますsed
が、sedスクリプトが長くなるほど読みやすくなり、6ヶ月以内に一目で理解できるコードを書くのが好きです。
比較的マイナーな詳細に加えて、両方のスクリプトは非常に似たアルゴリズムを使用します。