sed(또는 awk)를 사용하여 특정 필드에 동일한 값이 포함된 연속 행을 요약하시겠습니까?

sed(또는 awk)를 사용하여 특정 필드에 동일한 값이 포함된 연속 행을 요약하시겠습니까?

지금까지 돌아다닐 수 있었어요sed여러 줄에 걸쳐 앞으로/뒤로 보기와 ​​같은 고급 기능이 있지만 다음을 달성하는 방법을 알고 싶습니다.sed예를 들어 내가 하는 방식이 다음과 같다고 느끼기 때문입니다.파이썬필요하지 않으며 필터 덕트 내부에서도 수행 가능지침.

들어오는 데이터를 제거하는 예는 다음과 같습니다.

1b41cf70 0
1cb8dd19 1
620f0b67 2
620f0b67 3
f35d35fe 4
3a6fb62a 5
620f0b67 6
620f0b67 7
620f0b67 8
b958a7ea 9
f35d35fe 10
f35d35fe 11
620f0b67 12

첫 번째 열은 항상 동일한 너비(단축된 해시 포함)이고 두 번째 열의 내용은 완전히 정렬되고 숫자이며 간격이 없습니다(따라서 더 긴 목록에 방향을 제공하는 경우를 제외하고는 필요하지 않을 수 있습니다).

원하는 출력은 다음과 같습니다(마지막 연속 발생의 색인을 추가 열에 넣습니다).

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

또는 집계된 중복 값 수(수학적 표현(덧셈))를 사용하는 것이 더 나은 방법입니다.하지만 내 실력이 더 나쁘기 때문에 이것은 다른 이상적인 결과를 설명하기 위한 것입니다.)

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

SO 공간에서 찾은 유사하지만 다른 몇 가지 질문을 추적하려고 했지만 sed '$!N;/^\([^\ ]\+\)\ [0-9]\+\n\1\ /{P;d}' sampledata인덱스가 3,7,11인 이유와 같이 솔루션으로 이어질 수 있는 더 간단한 부분이 무엇인지 머리를 감쌀 수 없습니다. 행을 자르기 위해 Not 8 대신 사용됩니다.

내 시스템에는 GNU sed 버전 4.8과 awk 버전 5.1.0이 설치되어 있으며 그 중 하나를 사용하여 이 작업을 수행하는 방법을 알고 싶습니다. 아니요, 이것은 숙제가 아니라 압축하고 비교해야 하는 중복성이 많은 긴 해시 목록입니다. ;)

답변1

원래 두 번째 열을 완전히 무시하면 이를 사용하여 uniq -c연속 행에서 문자열이 반복되는 횟수를 계산할 수 있습니다.

두 필드의 출력을 사용하면 문자열이 여러 번 반복될 때마다 세 번째 필드를 만들 수 있습니다 uniq -c(테이블에서 필드 발생 횟수 +x에서 1을 뺀 값). x그런 다음 처음 두 필드를 다시 정렬하고 인쇄합니다.

cut -d ' ' -f 1 file |
uniq -c |
awk '$1 > 1 { $3 = "+" $1 - 1 } { nr += $1; $1 = $2; $2 = nr - 1 - $3; print }'

nr변수는 원본 파일의 줄 번호를 나타냅니다.

질문의 데이터 출력을 제공합니다.

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

답변2

사용 awk:

awk 'function prnt() { print buf, preV; preK=$1; preV=""; buf=$0 }
preK!=$1             { prnt(); next } { preV=$2 }
END                  { prnt() }' infile

산출:

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

awk 'function prnt() { print buf, (c?"+"c:""); preK=$1; c=0; buf=$0 }
preK!=$1             { prnt(); next } { c++ }
END                  { prnt() }' infile

산출:

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

답변3

당신은 그것을 요청했습니다 sed. 다음은 자신의 시도에 가까운 2가지 버전이지만 POSIX를 사용하는 것입니다.오히려 확장 정규식. 둘 다 패턴 공간에 최대 2줄을 유지합니다.

sed -E '
    :Q
    $!N
    /^([^ ]+) ([0-9]+)( [0-9]+)?\n\1 ([0-9]+)$/{
        s//\1 \2 \4/
        bQ
    }
    P
    D
' -- file

어디:

  • $!마지막 줄에 개행 문자를 추가하고( ) 현재 줄에 다음 줄을 추가하지 않는 한 ( N)
  • 일치 표현식은 /…/필드 1과 2를 \1및 로 캡처하고 \2가능한 마지막 인덱스는 이며 \3마지막 다음 행의 인덱스는 다음과 같습니다.\4
  • 필드 1이 다음 줄에서 반복되면 전체 패턴 공간이 필드 1(해시), 필드 2(첫 번째 인덱스) 및 마지막 인덱스로 대체되어 스크립트 시작 부분으로 분기됩니다. 명령의 빈 정규식은 다음과 s같습니다. 다시 적용됨 마지막 정규식 사용(in /…/)
  • 그렇지 않으면 첫 번째 줄( P;D;)을 인쇄하고 삭제하고 루프를 다시 시작합니다.

산출:

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

대신에:

/^([^ ]+) ([0-9]+)( ([+]+))?\n\1 [0-9]+$/{
    s//\1 \2 \4+/

출력은 다음과 같습니다.

1b41cf70 0
1cb8dd19 1
620f0b67 2 +
f35d35fe 4
3a6fb62a 5
620f0b67 6 ++
b958a7ea 9
f35d35fe 10 +
620f0b67 12

sed계산을 좋아하지는 않지만 할 수는 있습니다.


sed마지막으로 POSIX를 사용하는 스크립트에 대한 몇 가지 설명입니다.갈아 바수다에스

  • []이스케이프 문자를 제외하고 s 내의 문자를 이스케이프 하지 마십시오 . , ]및 가능한 이스케이프 문자-
  • BRE의 기호는 +수량자가 아니라 일반적인 더하기 기호입니다.
  • 공백 문자를 이스케이프 처리할 필요가 없습니다.
  • }이식성을 위해 편집 명령 목록을 끝내기 전에 세미콜론을 사용하십시오.
  • d명령은 첫 번째 개행 문자뿐만 아니라 전체 패턴 공간을 제거합니다.

답변4

빠르고 혼란스러운 여러 줄 찾기 및 바꾸기 솔루션(이번에는 Perl에서)

perl -0pe 's/(\w+) (\d+)(\n\1 (\d+))+/$1 $2 $4/g' file

해당 (gnu)sed 버전은 다음과 같습니다.

sed -rz 's/(\w+) ([0-9]+)(\n\1 ([0-9]+))+/\1 \2 \4/g' file

"+" 출력의 경우 몇 가지 추가 계산을 수행해야 합니다.

perl -0pe 's/(\w+) (\d+)(\v\1 (\d+))+/"$1 $2 +" . ($4-$2)/ge' file

관련 정보