파일에서 두 패턴 사이에 마지막으로 나타나는 줄을 얻는 방법은 무엇입니까?

파일에서 두 패턴 사이에 마지막으로 나타나는 줄을 얻는 방법은 무엇입니까?

프로세스의 출력을 보고하는 로그 파일이 있고 두 패턴의 마지막 발생에서 모든 행을 추출하고 싶습니다.

이러한 패턴은 다음과 같은 라인을 따릅니다.

Summary process started at <datestring>

그리고

Summary process finished at <datestring> with return code <num>

다른 많은 정보와 함께 파일 전체에 이러한 패턴의 여러 인스턴스가 있습니다. 마지막 항목만 인쇄하고 싶습니다.

다음을 사용할 수 있다는 것을 알고 있습니다.

sed -n '/StartPattern/,/EndPattern/p' FileName

패턴 사이의 선을 파악했지만 이를 얻는 방법을 모릅니다.마지막예.

sed아니면 awk해결책이 나올 것입니다.

편집하다:StartPatternsmultiple EndPattern. ​EndPatternStartPattern

  • StartPattern누락된 s가 여러 개인 경우 마지막 행부터 까지의 행만 EndPattern원합니다 .StartPatternEndPattern
  • 안 오는 건 StartPattern다 왔으면 좋겠고, 그 다음에는 조기 도착 주의가 있어요.EOFEndPatternEOFEOF

답변1

언제든지 다음과 같이 할 수 있습니다.

tac < fileName | sed  '/EndPattern/,$!d;/StartPattern/q' | tac

시스템에 GNU가 없다면 tac이를 사용할 수도 있습니다 tail -r.

다음과 같이 할 수도 있습니다.

awk '
  inside {
    text = text $0 RS
    if (/EndPattern/) inside=0
    next
  }
  /StartPattern/ {
    inside = 1
    text = $0 RS
  }
  END {printf "%s", text}' < filename

그러나 이는 전체 파일을 읽는 것을 의미합니다.

StartPatterna와 다음 사이에 다른 것이 있거나, 마지막 것이 끝나지 않거나, a와 일치하는 줄이 있는 경우에는 다른 결과가 나타날 수 있습니다 .StartPatternEndPatternStartPatternEndPatternStartPatternEndPattern

awk '
  /StartPattern/ {
    inside = 1
    text = ""
  }
  inside {text = text $0 RS}
  /EndPattern/ {inside = 0} 
  END {printf "%s", text}' < filename

tac+sed+tac(닫히지 않은 후행 사례를 제외하고 ) 해당 방법과 더 유사하게 작동하게 됩니다 StartPattern.

마지막 항목이 편집자가 원하는 것과 가장 가까운 것 같습니다. 경고를 추가하려면 다음을 수행하십시오.

awk '
  /StartPattern/ {
    inside = 1
    text = ""
  }
  inside {text = text $0 RS}
  /EndPattern/ {inside = 0} 
  END {
    printf "%s", text
    if (inside)
      print "Warning: EOF reached without seeing the end pattern" > "/dev/stderr"
  }' < filename

전체 파일을 읽지 않으려면 다음을 수행하십시오.

tac < filename | awk '
  /StartPattern/ {
    printf "%s", $0 RS text
    if (!inside)
      print "Warning: EOF reached without seeing the end pattern" > "/dev/stderr"
    exit
  }
  /EndPattern/ {inside = 1; text = ""}
  {text = $0 RS text}'

이식성 참고: /dev/stderr)에 대한 특수 파일이 있는 시스템이나 awk이를 에뮬레이트하는 구현(예: gawk, mawkbusybox) 이 필요합니다 awk(위에서 언급한 Linux 문제를 해결함).

print ... > "/dev/stderr"다른 시스템에서는 로 바꿀 수 있습니다 print ... | "cat>&2".

답변2

sedGNU를 이렇게 사용할 수 있습니다

sed '/START/{:1;$!{/END/!{N;b1};h}};${x;p};d' file

전체 여러 줄 패턴이 나타날 때마다 예약된 공간을 덮어쓰세요. 파일 끝에 인쇄하세요.

이는 일관된 동작을 제공합니다.

  • 같은 줄에 있는 START와 END는 모두 줄과 일치합니다.
  • 초기 START 이후의 여러 START는 END까지 모두 일치합니다.
  • END가 없으면 일치 항목이 인쇄되지 않으며 전체 START에서 END까지의 마지막 항목이 인쇄됩니다.

답변3

의 경우 GNU sed다른 솔루션은 다음과 같습니다(변수 P1/를 P2시작/종료 패턴으로 사용).

sed -n "/${P1}/,/${P2}/H; /${P1}/h; \${g;p}"

@Stéphane Chazelas의 솔루션과의 주요 차이점은 다음과 같습니다.

  • 마지막 END/EOF 이전에 여러 개의 START가 있는 경우 마지막 START부터 마지막 ​​END/EOF까지 표시됩니다.
  • START와 같은 줄에 있는 모든 END는 무시됩니다.
  • 마지막 입력 라인에서 마지막 END 지원
  • 마지막 START 이후에 END가 없으면 마지막 START부터 EOF까지 인쇄합니다.

답변4

awk의 솔루션은 다음과 같습니다.

awk '/EndPattern/ {recording=0}  recording>0 {buffer=buffer $0 "\n"}  /StartPattern/ {recording+=1; buffer=""}  END {printf "%s", buffer; if(recording>0) {print "WARNING: missing EndPattern" > "/dev/stderr"}}'

따라서 다음 입력의 경우:

1
StartPattern
2
3
EndPattern
4
5
StartPattern
6
7
EndPattern
8

다음과 같은 결과가 출력됩니다.

6
7

정확한 라인 일치를 원할 경우 StartPattern을 ^StartPattern$로 바꾸고 EndPattern도 마찬가지입니다. 중첩된 패턴을 무시하려면 Recording+=1을 Recording=1로 바꿀 수도 있습니다.

관련 정보