Как идентифицировать, отсортировать по убыванию и отобразить 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) для поиска блоков текста, принадлежащих «категории», сортировки результирующих блоков в порядке убывания индекса (чисел), отображения количества блоков Я просил.

Например, если я хочу выполнить поиск в 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

Как я могу этого добиться. Любая помощь будет принята с благодарностью: -)

0
17.10.2016, 06:57
4 ответа

Чтобы следовать принципу 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 строка в нулевой символ ( \ x0 ), а затем преобразована обратно tr .
Итак, вся строка:

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 изменить R ow S eparator, но это немного подходит в случае. Проще сделать это обычным способом

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
0
28.01.2020, 04:49

попробуйте изменить значения 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
-1
28.01.2020, 04:49

Предполагая, что начало вашего блока можно определить по строке из 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'

awk соединяет строки, если поле $ NF (последнее поле) является желаемой категорией. Переменная sep изначально пуста "" затем становится \ 001 внутри блока и \ n , когда начинается новый блок. В конце добавляется последняя строка новой строки, если не было совпадений.

0
28.01.2020, 04:49

Вот решение для 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]' устанавливает RS awk (разделитель записей) переменную в регулярное выражение, состоящее из новой строки, , за которой следует целочисленный порядковый номер до четырех цифр, , за которым следует пробел. Я добавил новую строку, потому что ваши данные имеют четырехзначные числа (с пробелами) внутри строк, где они не должны интерпретироваться как разделители записей. Обратите внимание, что это регулярное выражение немного небрежно, так как оно принимает 007 и 12 4 .

    Установка этого параметра в качестве разделителя записей awk означает, что каждая из ваших «транзакций» будет рассматриваться как одна запись awk, даже если она содержит несколько строк. Есть несколько недостатков:

    • Поскольку шаблон RS включает новую строку в начале, 1 в начале ваших данных не будет распознан как разделитель записей.
    • Поскольку это шаблон разделителя записей , он не считается частью записи, даже если он содержит важную информацию.

    Мы разберемся с этими проблемами.

  • -vcat = "$ cat" и -vnum = "$ num" аналогичным образом устанавливают переменные awk cat и num к значениям соответствующих переменных оболочки.
  • НАЧАТЬ {первый = 1; rec_ind = 0} инициализирует флаг first значением true (1), , чтобы мы могли распознать первую запись и обработать ее специально, и индекс записи ( rec_ind ) на 0, для накопления записей, соответствующих желаемой категории.
  • если (first) истинно (мы обрабатываем первую запись), установить rec равным записи awk, $ 0 . Помните, что сюда входят все строки до (но не включая) следующей строки, которая начинается с четырехзначного числа. Кроме того, он включает четырехзначное число в начале первой строки. Затем мы устанавливаем флаг первый в значение false (0).

    Если это не первая запись, тогда отсутствует ее четырехзначный номер (потому что это разделитель записи ), поэтому мы создаем запись ( rec ) путем объединения сохраненного порядкового номера ( save_seq ) с $ 0 . (Я сейчас расскажу о save_seq .)

  • findnl = index (rec, "\ n") находит первую новую строку в записи (помните, записи содержат несколько строк). Если от начала меньше 7 символов, тогда нет места для порядкового номера и категории (без перекрытия), не говоря уже о других полях, так что это ошибка. В противном случае извлеките категорию этой записи ( thiscat ) из последних трех символов перед первой новой строкой - т. Е. Последних трех символов строки первая строка сделки. Затем, если thiscat соответствует категории, которую мы ищем, сохраните запись в массиве records .
  • RT - признак конца записи - символы, соответствующие шаблону RS в конце текущей записи. К сожалению, ограничитель текущей записи на самом деле является началом следующей. Если текущая запись является последней, тогда RT будет пустой строкой (длина 0); в противном случае она всегда должна состоять из 6 символов long (новая строка, четыре символа, которые являются пробелами или цифрами, и пробел). Извлеките последние пять символов (т.е. отбросьте новую строку) и сохраните их как save_seq , , потому что это порядковый номер следующей транзакции.
  • Когда мы дойдем до конца данных, отсортируйте записи (сортировка значений, обработка их как чисел в порядке убывания). Затем выведите до num из них.
1
28.01.2020, 04:49

Теги

Похожие вопросы