카테고리별로 상위 10개 텍스트 블록을 식별하고 내림차순으로 정렬하고 표시하는 방법

카테고리별로 상위 10개 텍스트 블록을 식별하고 내림차순으로 정렬하고 표시하는 방법

당사 시스템 내 개체에 대해 완료된 거래 내역은 다음과 같습니다.

   1 BYM1 TSTAB 09NOV 0035 CAB
Sometext 01
   2 BYM1 TSTAB 09NOV 0035 CAB
Can be done - question   
   3 BYM1 TSTAB 09NOV 0035 CAB
Sometext 02
Sometext 03
   6 BYM3 TSTAA 09NOV 0400 CAA
Some 04 text 04
   7 BYM3 TSTAA 10NOV 0455 CAC
Sometext 06
Sometext 06 line 2
   8 BYM3 TSTAA 10NOV 0455 CAC
Sometext 07
   9 BYM2 TSTAC 10NOV 0619 CAD
Some 08 text 0008 ABCD
Some 08 text 0008 BB00
Some 08 text 0008 CC00
Some 08 text 0008 DD00
Some 08 text 0008 EE00
  10 BYM2 TSTAC 10NOV 0627 CAD
Something BBBBBSSDGFSDSF
  11 BYM2 TSTAC 10NOV 0627 CAD
Something else
  12 BYM2 TSTAC 10NOV 0627 CAD
What text here
  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out
  19 BYM3 TSTAA 11NOV 0438 CAE
Some 04 text 05 05 05
  20 BYM3 TSTAA 11NOV 0441 CAF
Not so confidential now
  21 BYM3 TSTAA 11NOV 0441 CAF
Some 00 text 0009 X1X2
  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD
  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text
  45 BYM3 TSTAA 12NOV 1455 CAC
Something 0AADDBB
8782 BYM3 TSTAA 12NOV 1610 CAD
Something 0AADDBB
8830 BYM3 TSTAA 12NOV 1612 CAA
Something 0AADDBB
9999 BYM3 TSTAA 12NOV 1722 CAA
Something 0AADDBB

텍스트 블록은 처음 4자가 숫자를 포함하는 줄로 시작됩니다. (이 번호는 실제로 각 거래가 색인화되는 일련번호입니다.) 블록(트랜잭션) 카테고리는 숫자가 포함된 줄의 마지막 세 문자로 정의됩니다.

"범주"에 속하는 텍스트 블록을 검색하고 결과 블록을 인덱스(번호)의 내림차순으로 정렬하고 필요한 블록 수를 표시하는 awk, sed(, vi, grep) 스크립트를 찾고 있습니다. 주문하다.

예를 들어 "CAD" 카테고리의 블록 4개를 검색하려는 경우 보고 싶은 결과는 다음과 같습니다.

8782 BYM3 TSTAA 12NOV 1622 CAD
Something 0AADDBB
  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text
  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD
  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out

어떻게 이것을 달성할 수 있습니까? 어떤 도움이라도 대단히 감사하겠습니다 :-)

답변1

이것은 gawk(GNU, 즉 대부분의 "Linux" 시스템의 버전 ) awk에 대한 솔루션 입니다. 검색하려는 카테고리로 설정하고, 표시하려는 레코드 수로 설정했다고 awk가정해 보겠습니다 .$cat$num

awk -vRS='\n[ 0-9][ 0-9][ 0-9][0-9] ' -vcat="$cat" -vnum="$num" \
    '   BEGIN { first=1; rec_ind=0}
        {       if (first) {
                        rec = $0
                        first=0
                } else {
                        rec = save_seq $0
                }
                findnl = index(rec, "\n")
                if (findnl < 7) exit
                thiscat = substr(rec, findnl-3, 3)
                if (cat == thiscat) records[++rec_ind] = rec
                if (length(RT) == 0) {
                        # print "This should be the last record."
                        save_seq = "Does not matter"
                } else if (length(RT) == 6) {
                        save_seq = substr(RT, 2, 5)
                } else {
                        print "Invalid RT: len =", length(RT)
                        exit
                }
        }
        END   { num_recs = asort(records, sorted_records, "@val_num_desc")
                if (num < num_recs) num_recs = num
                for (i=1; i<=num_recs; i++) {
                        print sorted_records[i]
                }
              }
    '

노트:

  • -vRS='\n[ 0-9][ 0-9][ 0-9][0-9] 'awk의 RS(레코드 구분 기호) 변수를 개행 문자, 최대 4자리의 정수 시퀀스 번호 및 공백으로 구성된 정규식으로 설정합니다. 데이터 줄 안에 4자리(공백이 뒤따름)가 있고 레코드 구분 기호로 해석되지 않기 때문에 줄 바꿈을 포함합니다. 이 정규식은  007sums 을 허용하므로 약간 엉성합니다 12 4.

    이것을 awk의 레코드 구분 기호로 설정하면 각 "트랜잭션"이 여러 행을 포함하더라도 단일 awk 레코드로 처리됩니다. 몇 가지 단점이 있습니다:

    • RS 패턴의 시작 부분에 개행 문자가 포함되어 있으므로    1 시작 부분의 데이터는 레코드 구분 기호로 인식되지 않습니다.
    • 이건 기록이니까분할기스키마에 중요한 정보가 포함되어 있더라도 레코드의 일부로 간주되지 않습니다.

    우리는 이러한 문제를 다룰 것입니다.

  • -vcat="$cat"그리고 -vnum="$num"마찬가지로 awk 변수 catnum값을 해당 쉘 변수에 설정합니다.
  • BEGIN { first=1; rec_ind=0}first첫 번째 레코드를 식별하고 특별하게 처리할 수 있도록 플래그를 true(1)로 초기화하고 , rec_ind원하는 카테고리에 일치하는 레코드가 누적되도록 레코드 인덱스( )를 0으로 초기화합니다.
  • if (first)true(첫 번째 레코드를 처리 중)이고 recawk records와 동일하게 설정됩니다 $0. 여기에는 4자리 숫자로 시작하는 다음 줄까지의 모든 줄이 포함됩니다(포함되지 않음). 또한 첫 번째 줄 시작 부분에 4자리 숫자가 포함됩니다. 그런 다음 first플래그를 false(0)로 설정합니다.

    첫 번째 레코드가 아닌 경우 4자리가 누락된 것입니다(이 레코드는분할기rec)이므로 저장된 일련 번호( )를 와 save_seq연결하여 레코드( )를 구성합니다 $0. ( save_seq이에 대해서는 나중에 논의하겠습니다.)

  • findnl = index(rec, "\n")레코드에서 첫 번째 개행 문자를 찾습니다(레코드에는 여러 줄이 포함되어 있음을 기억하세요). 처음부터 7글자 미만이면 다른 항목은 물론이고 일련번호와 카테고리(겹치지 않고)를 넣을 자리가 없어 오류가 발생합니다. 그렇지 않으면 thiscat첫 번째 개행 문자 앞의 마지막 세 문자(즉, 트랜잭션 첫 번째 줄의 마지막 세 문자)에서 레코드의 범주( )를 추출합니다. 그런 다음 thiscat찾고 있는 카테고리와 일치하면 레코드가 records배열에 저장됩니다.
  • RTRS레코드 종료자(현재 레코드 끝의 패턴과 일치하는 문자)입니다. 불행하게도 현재 레코드의 끝은 실제로 다음 레코드의 시작입니다. 현재 레코드가 마지막 레코드인 경우 이는 RT빈 문자열(길이 0)이 됩니다. 그렇지 않은 경우 길이는 항상 6자(개행 문자 1개, 공백 또는 숫자 4개, 공백 1개)여야 합니다. 마지막 5개 문자를 추출하고(예: 개행 문자 삭제) save_seq다음 트랜잭션의 시퀀스 번호이므로 로 저장합니다.
  • 데이터 끝에 도달하면 레코드를 정렬합니다(값을 숫자로 처리하여 내림차순으로 정렬). 그런 다음 num그 중 최대 하나를 인쇄하십시오 .

답변2

Linux 원칙 "하나의 작업 - 하나의 도구"를 따르십시오.

  1. 필요한 블록만 인쇄합니다(예제 참조 CAD).

    sed '/^\s*[0-9].*CAD/!d;:a;N;/\n\s*[0-9]/! s/\n/\x0/;ta;P;D'

  2. 역순으로 정렬

    sort -rn

  3. 첫 번째 요청된 청크만 가져옵니다(예제 참조 4).

    head -4

대부분의 Linux 명령은 다음을 통해 실행됩니다.철사(아니요조각\n) 따라서 ew 라인을 null-symbol()로 변경하여 이를 라인으로 변환한 \x0다음 전달합니다..
따라서 모든 줄은 다음과 같습니다.

sed '/^\s*[0-9].*CAD/!d;:a;N;/\n\s*[0-9]/! s/\n/\x0/;ta;P;D' test.txt |
sort -rn |
head -4 |
tr '\0' '\n'

좋아요G맨 답변R흐름 분리기의 아이디어를 바꾸었지만 S이 상황에는 잘 맞지 않습니다. 일반적인 방법으로 하면 더 쉽죠

awk '
/^[ 0-9]{4} /{                 #for start block string
    if($NF==cat){              #if it is a needed block
        idx=$1
        BLOCK[idx]=$0          #put line onto array with asigned index
    }
    else
        idx=0                  #otherways asign index to 0
    next                       #end itteration, go to start with next line
}
idx{                           #pass inappropriate blocks (with 0-index)
BLOCK[idx]=BLOCK[idx] "\n" $0  #add line to array element with index
}
END{                           #when finish all lines
    for(i=0;i<num;i++){        #do num times
        max=0                  #asing `max` variable to min value
        for(idx in BLOCK){     #for each index in array
            idx=idx+0          #convert string index into decimal
            if(idx>max)        
                max=idx        #find maximum index (field No.1 in block)
        }
        if(!max)             
            exit               #exit script if array empty (no more blocks)
        print BLOCK[max]       #print block with maximum index
        delete BLOCK[max]      #remove array element for furure search
     }
}' cat="CAD" num=4 test.txt

답변3

예를 들어, 숫자로 시작하는 6개 필드의 라인으로 블록 시작을 감지할 수 있고 데이터에 문자 코드 \001(control-a)가 포함되어 있지 않다고 가정하면 블록의 모든 라인을 한 라인으로 연결하여 임의의 필드를 대체할 수 있습니다. 코드의 개행 문자입니다. 그런 다음 줄을 정렬하고 처음 4줄을 가져와서 코드를 다시 개행 문자로 바꿉니다.

#!/bin/bash
num=${1?number} cat=${2?category}
awk -vcat="$cat" '
 /^ *[0-9]+ / && NF==6 { ok = ($NF==cat) 
                         if(ok && sep!="")sep = "\n"
                       }
                    ok { printf "%s%s",sep,$0; sep = "\001" }
                   END { if(sep!="")printf "\n" }' |
sort -nr -k1,1 | head -"$num" |
tr '\001' '\n'

$NF필드(마지막 필드)가 필수 카테고리인 경우 awk는 행을 연결합니다. 이 sep변수는 처음에는 비어 있다가 블록 내부에 ""들어가고 새 블록이 시작될 때 사용됩니다. 일치하는 항목이 없으면 마지막 개행 문자가 마지막에 추가됩니다.\001\n

답변4

v= 및 num= 값을 변경해 보세요.

$ awk '$NF==v{F=1;print;next}F&&NF!=6{print}F&&NF==6{F=0}' v="CAC" test.txt | awk '$NF~v{val=j++;F=1}F{Arr[val]=Arr[val]"\n"$0}END{n=asorti(Arr,S_Arr);for(i=n;i>=n-num;i--){print Arr[i]}}' v="CAC" num=4


  45 BYM3 TSTAA 12NOV 1455 CAC
Something 0AADDBB

   8 BYM3 TSTAA 10NOV 0455 CAC
Sometext 07

   7 BYM3 TSTAA 10NOV 0455 CAC
Sometext 06
Sometext 06 line 2

$ awk '$NF==v{F=1;print;next}F&&NF!=6{print}F&&NF==6{F=0}' v="CAD" test.txt | awk '$NF~v{val=j++;F=1}F{Arr[val]=Arr[val]"\n"$0}END{n=asorti(Arr,S_Arr);for(i=n;i>=n-num;i--){print Arr[i]}}' v="CAD" num=4


8782 BYM3 TSTAA 12NOV 1610 CAD
Something 0AADDBB

  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text

  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD

  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out

관련 정보