Сортировка порций строк последовательно нескольких входных файлов с использованием сопоставления с образцом - PullRequest
0 голосов
/ 28 августа 2018

У меня есть фрагменты данных, распределенные по 100 файлам, которые при повторной сортировке следуют числовой последовательности. Например, если у меня есть 100 блоков данных, блок № 1, 3, 5 может быть в одном файле, а блок № 2, 4, 6 может быть в другом. Мне нужно создать 1 выходной файл со всеми кусками в последовательном порядке: # 1,2,3,4,5,6.

Ниже приведена сокращенная версия 2 (из 100) входных файлов. Каждый фрагмент начинается с «ITEM: TIMESTEP» и должен быть упорядочен по номеру в следующей строке (здесь это 1000, 2000, 3000, 4000).

ВХОДНОЙ ФАЙЛ 1

ПУНКТ: TIMETEP
1000
ПУНКТ: ЧИСЛО АТОМОВ
50 2 H 0,4 0,3 0,006
10214 2 H 0,5 0,4 0,002
...... # 12 000 строк спустя # ...
ПУНКТ: TIMETEP
3000
ПУНКТ: ЧИСЛО АТОМОВ
50 2 H 2,3 1,4 0,3
10214 2 H 2,5 1,3 0,6
...... # 12 000 строк спустя # ...

ВХОДНОЙ ФАЙЛ 2

ПУНКТ: TIMETEP
2000
ПУНКТ: ЧИСЛО АТОМОВ
50 2 H 0,4 0,3 0,006
10214 2 H 0,5 0,4 0,002
...... # 12 000 строк спустя # ...
ПУНКТ: TIMETEP
4000
ПУНКТ: ЧИСЛО АТОМОВ
50 2 H 2,3 1,4 0,3
10214 2 H 2,5 1,3 0,6
...... # 12 000 строк спустя # ...

Конечный выходной файл будет выглядеть так

ПУНКТ: TIMETEP
1000
.... # остаток чанка # ...
ПУНКТ: TIMETEP
2000
.... # остальная часть куска # ...
ПУНКТ: TIMETEP
3000
.... # остальная часть куска # ...
ПУНКТ: TIMETEP
4000
.... # остальная часть куска # ...

До сих пор я вставлял строку идентификатора с именем «IDENTIFIER» перед началом каждого блока:

awk -v n=12,000 '1; NR%n==0 {print "IDENTIFIER"}' in.txt >> out1.txt

И я могу напечатать N строк, необходимых для каждого чанка, который следует за каждой строкой идентификатора, с циклическим просмотром нескольких файлов

for i in $(seq 1000 1000 10000); do
  awk 'c&&c--;/IDENTIFIER/{c=12,000}' out${i}.txt >> out-final.txt
done

Я использовал этот метод, чтобы специально идентифицировать 2-й ряд каждого чанка, потому что эти числа могут повторяться внутри самого чанка. Однако я не знаю, как изменить вторую командную строку, чтобы она выводилась только в out-final.txt, когда значение после IDENTIFIER является следующим числом в последовательности.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Я бы использовал Perl для этого

cat file{1,2} | perl -0777 -ne '
    @records = split /^(?=ITEM: TIMETEP)/m;
    print join "",
        map  { $_->[1] }
        sort { $a->[0] <=> $b->[0] }
        map  { ($n) = /\n(\d+)\n/; [$n, $_] }
        @records;
'

Опция -0777 заставляет Perl слить все входные данные в одну строку. Мы используем заголовок для разделения на записи. Затем преобразование Шварца для сортировки, снова объедините записи и напечатайте.


Если вам нравится боль, вот версия с одним шумом для одной строки:

cat file{1,2} | perl -0777 -pe'$_=join"",map{$_->[1]}sort{$a->[0]<=>$b->[0]}map{[/\n(\d+)\n/,$_]}split/^(?=ITEM: TIMETEP)/m'
0 голосов
/ 29 августа 2018

Префикс каждой записи с идентификатором записи из строки 2 каждой записи и номером строки с начала этой записи, сортировка по этому идентификатору записи и номеру строки, а затем удаление их после сортировки:

$ cat tst.sh
awk '
    BEGIN { OFS="\t" }
    /^ITEM: TIMETEP/ { head=$0; lineNr=1; next }
    lineNr == 1 { recId=$0; print recId, lineNr, head }
    { print recId, ++lineNr, $0 }
' "$@" |
sort -k1,2n |
cut -f3-

$ ./tst.sh file1 file2
ITEM: TIMETEP
1000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
2000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
3000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...
ITEM: TIMETEP
4000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...

Поскольку единственная команда, описанная выше, которая обрабатывает все входные данные "сразу" (в отличие от строки за строкой), это sort, она будет работать для большого количества больших файлов, поскольку sort предназначена для подкачки страниц, и т. д. для обработки большого ввода (см. https://unix.stackexchange.com/a/279099/133219).

0 голосов
/ 28 августа 2018

Я предлагаю другой подход: сначала разбить файлы, чтобы каждый элемент находился в своем собственном файле, а затем объединить файлы в нужном порядке. Например для данных двух файлов

$ awk '/^ITEM: TIMETEP/{h=$0; next} 
                     h {f="item_"$0; print h > f; h=""} 
                       {print > f}' file1 file2 

создаст четыре экстракта, которые можно объединить, просто

$ cat item_{1..4}000 > merged_items
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...