awk를 사용하여 BASH의 두 줄 비교

awk를 사용하여 BASH의 두 줄 비교

이런 텍스트 파일이 있습니다.

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)을 사용하여 현재 행과 이전 행을 보유하는 것입니다. 두 줄의 세 번째 필드(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버전은 두 줄의 필드 3이 정확히 동일한지 명시적으로 확인하는 반면, 이 sed버전은 IP 주소와 유사해 보이는 패턴이 나중에 동일한 연결의 두 줄에서 반복되는지만 확인합니다(반드시 문제가 되는 것은 아닙니다. 귀하의 예제 입력, 이 sed버전은 귀하의 예제에 완벽하게 최적화되어 있습니다).

perl버전은 다양한 입력에 적응하기가 더 쉬울 수 있습니다.

#루프 시작 부분의 코드는 빈 줄을 건너뛰고 텍스트 파일의 주석을 지원하기 위해 많은 Perl 스크립트에서 사용하는 유용한 코드 조각입니다 . 나는 종종 스크립트에서 동일한 작업을 수행 sed하지만 sed 스크립트가 길어질수록 가독성이 떨어지게 되고... 그리고 6개월 안에 한 눈에 이해할 수 있는 코드를 작성하는 것을 좋아합니다.

상대적으로 사소한 세부 사항 외에도 두 스크립트는 매우 유사한 알고리즘을 사용합니다.

관련 정보