awkを使用したBASHの2行の比較

awkを使用したBASHの2行の比較

このようなテキストファイルがあります。

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ヶ月以内に一目で理解できるコードを書くのが好きです。

比較的マイナーな詳細に加えて、両方のスクリプトは非常に似たアルゴリズムを使用します。

関連情報