Text-Processing

如何按類別辨識、降序排序和顯示前 10 個文本塊

  • October 17, 2016

在我們系統內的實體上完成的交易歷史如下所示:

  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) 腳本來搜尋屬於“類別”的文本塊,按索引(數字)的降序對結果塊進行排序,顯示我問過的塊數為了。

例如,如果我想搜尋 4 個類別“CAD”的塊,我想看到的輸出是:

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

我怎樣才能做到這一點。任何幫助將不勝感激 :-)

遵循 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 命令使用(而不是blocks)操作,因此通過將\new line 更改為 null-symbol( ) 將這些命令轉換為行,然後通過tr\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-Man answer改變Row Separator 的想法,但這不太適合這種情況。用普通方法做更簡單

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

這是 gawk 的解決方案(GNU awk;即awk大多數“Linux”系統上的版本)。假設$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(記錄分隔符)變數設置為由換行符組成的正則表達式,後跟最多四位的整數序列號,後跟一個空格。我包含換行符是因為您的數據在行的內部有四位數字(後跟空格),它們不會被解釋為記錄分隔符。請注意,這個正則表達式有點草率,因為它會接受007and 12 4

將此設置為 awk 的記錄分隔符意味著您的每個“事務”都將被視為單個 awk 記錄,即使它包含多行。有幾個缺點:

  • 由於 RS 模式在開頭包含換行符,因此1數據開頭的 將不會被辨識為記錄分隔符。
  • 由於這是記錄分隔符模式,因此它不被視為記錄的一部分,即使它包含重要資訊。我們會處理這些問題。
  • -vcat="$cat"-vnum="$num"類似地設置 awk 變數 catnum相應 shell 變數的值。
  • BEGIN { first=1; rec_ind=0}將標誌初始化first為真(1),因此我們可以辨識第一條記錄並對其進行特殊處理,並將記錄索引(rec_ind)設置為0,用於累積匹配所需類別的記錄。
  • if (first)為真(我們正在處理第一條記錄),設置rec為等於 awk 記錄,$0. 請記住,這包括直到(但不包括)以四位數開頭的下一行的所有行。此外,它還包括第一行開頭的四位數字。然後我們將first標誌設置為假(0)。

如果這不是第一條記錄,那麼它缺少它的四位數字(因為那是記錄分隔符),所以我們rec通過將保存的序列號 ( save_seq) 與連接來構造記錄 ( ) $0。(我會save_seq暫時討論。)

  • findnl = index(rec, "\n")查找記錄中的第一個換行符(請記住,記錄包含多行)。如果從頭到尾少於 7 個字元,那麼序號和類別(沒有重疊)就沒有空間,更不用說其他欄位了,所以這是一個錯誤。否則,從第一個換行符之前的最後三個字元中提取此記錄的類別 ( thiscat),即事務第一行的最後三個字元。然後,如果thiscat匹配我們正在尋找的類別,則將記錄保存在records數組中。
  • RT是記錄終止符——與RS目前記錄末尾的模式匹配的字元。不幸的是,目前記錄的終結者實際上是下一個記錄的開始。如果目前記錄是最後一條,則為RT空字元串(長度為 0);否則,它應始終為 6 個字元長(一個換行符、四個空格或數字字元以及一個空格)。提取最後五個字元(即丟棄換行符)並將其另存為save_seq,因為它是下一個事務的序列號。
  • 當我們到達數據的末尾時,對記錄進行排序(對值進行排序,將它們視為數字,按降序排列)。然後列印num它們。

引用自:https://unix.stackexchange.com/questions/316867