История транзакций, выполненных на объекте в наших системах, выглядит следующим образом:
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 «одна задача - один инструмент»:
Печатает только необходимый блок (как в примере CAD
)
sed '/ ^ \ s * [0-9] . * CAD /! D;: a; N; / \ n \ s * [0-9] /! s / \ n / \ x0 /; ta; P; D '
Сортировка в обратном порядке
sort -rn
Возьмите только первые запрошенные блоки (как в примере 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
попробуйте изменить значения 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
Предполагая, что начало вашего блока можно определить по строке из 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
, когда начинается новый блок. В конце добавляется последняя строка новой строки, если не было совпадений.
Вот решение для 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, даже если она содержит несколько строк. Есть несколько недостатков:
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
из них.