Подумайте это то, что вы хотите, используя GNU sed
sed -n '/^PATTERN_START/,/^PATTERN_END/{
//!{H;/^Record/!{x;s/\n\([^\n]*\)$/ \1/;x}};
/^PATTERN_START/{h};/^PATTERN_END/{x;p;x;p};d
};p' file
sed -n #Non printing
'/^PATTERN_START/,/^PATTERN_END/{
#If the line falls between these two patterns execute the next block
//!{
#If the previous pattern matched from the line above is not on matched(so skip
the start and end lines), then execute next block
H;
#append the line to the hold buffer, so this appends all lines between
#`/^PATTERN_START/` and `/^PATTERN_END/` not including those.
/^Record/!{
#If the line does not begin with record then execute next block
x;s/\n\([^\n]*\)$/ \1/;x
#Swap current line with pattern buffer holding all our other lines
#up to now.Then remove the last newline. As this only executed when
#record is not matched it just removes the newline from the start
#of `data`.
#The line is then put switched back into the hold buffer.
}
#End of not record block
};
#End of previous pattern match block
/^PATTERN_START/{h};
#If line begins with `PATTERN_START` then the hold buffer is overwritten
#with this line removing all the previous matched lines.
/^PATTERN_END/{x;p;x;p}
#If line begins with `PATTERN_END` the swap in our saved lines, print them,
#then swap back in the PATTERN END line and print that as well.
;d
#Delete all the lines within the range, as we print them explicitly in the
#Pattern end block above
};p' file
# Print everything that's not in the range print, and the name of the file
Другие способы с sed
:
sed '/PATTERN_START/,/PATTERN_END/{ # in this range
//!{ # if not start or end of range
/^Record/{ # if line matches Record
x # exchange pattern and hold space
/^$/d # if pattern space is empty, delete it
s/\n/ /g # replace newlines with spaces
}
/^Record/!{ # if line doesn't match Record
H # append it to hold space
d # then delete it
}
}
/PATTERN_END/{ # at end of range
x # exchange pattern and hold space
s/\n/ /g # replace newlines with space
G # append hold space to pattern space
x # exchange again
s/.*// # empty pattern space
x # exchange again > empty line in hold space
}
}' infile
или
sed '/PATTERN_START/,/PATTERN_END/{ # same as above
//!{ # same as above
: again
N # pull the next line into pattern space
/\nRecord/!{ # if pattern space doesn't match this
/\nPATTERN_END/!{ # and doesn't match this either
s/\n/ / # replace newline with space
b again # go to : again
}
}
P # print up to first newline
D # then delete up to first newline
}
}' infile
В одной строке:
sed '/PATTERN_START/,/PATTERN_END/{//!{/^Record/{x;/^$/d;s/\n/ /g};/^Record/!{H;d}};/PATTERN_END/{x;s/\n/ /g;G;x;s/.*//;x}}' infile
и
sed '/PATTERN_START/,/PATTERN_END/{//!{: again;N;/\nRecord/!{/\nPATTERN_END/!{s/\n/ /;b again}};P;D}}' infile
Перл путь:
perl -lne 'if(/^PATTERN_START/){$i=1; %data=(); print; next};
if(/^PATTERN_END/){
$i=0;
if(defined($r)){
print "$_ @{$data{$_}}" for sort keys(%data)
}
}
if($i==1){
if(/^(Record\s*\d+)/){$r=$1;}
else{push @{$data{$r}},$_}
}
else{print}' file
И то же самое, что и закомментированный скрипт:
#!/usr/bin/env perl
## Read the input file line by line (this is the same as the `-n` flag)
while (<>) {
## If this line matches the start pattern, set $i to 1, clear
## the %data hash, print the line and skip to the next one.
if(/^PATTERN_START/){
$i=1;
%data=();
print;
next
}
## If this line matches the end pattern, set $i to 0
if(/^PATTERN_END/){
$i=0;
## If we're at the end pattern, we may have saved some
## data. This is the case if $r is defined.
if(defined($r)){
## Print the current line, then the saved data associated
## with each of the records of this section.
print "$_ @{$data{$_}}\n" for sort keys(%data)
}
}
## If $i is 1, if we're inside a START and END pattern.
if($i==1){
## Remove trailing newlines. This is equivallent to `-l` with
## the difference that `-l` also adds a newline to each print()
## call. We will add them manually here.
chomp;
## If this line starts with Record, 0 or more spaces and one
## or more numbers, save that as $r.
if(/^(Record\s*\d+)/){$r=$1;}
## If this line does not start with the above pattern, add it
## to the data saved for the current record.
else{push @{$data{$r}},$_}
}
## For all other lines, print them as is.
else{print}
}
Это лучшее, что я мог придумать:
sed -n '/^PATTERN_START/, /^PATTERN_END/{
/^PATTERN_START/{x;s/^.*$//;x};
/^Record/{x;/^\n/{s/^\n//p;d};s/\n/ /gp};
/^PATTERN_END/{x;/^\n/{s/^\n//p;d};s/\n/ /gp;g;p};
/^Record/!H
};
/^PATTERN_START/, /^PATTERN_END/!p'
Объяснение
Полагаю, вы знакомы с идеей удерживаемого пространства и пространства шаблонов в sed
. В этом решении мы будем выполнять множество манипуляций в пространстве шаблонов. Итак, во-первых, отключите автоматическую печать с помощью параметра -n
и печатайте везде, где это необходимо.
Первая задача - соединить все строки, которые находятся между Record
строками.
Рассмотрим следующий файл:
a
b
Record 1
c
d
Record 2
e
f
Record 3
После объединения строк мы хотим, чтобы он был
a
b
Record 1 c d
Record 2 e f
Record 3
Итак, вот план:
Запись
, это означает, что предыдущая запись закончилась и началась новая запись. Итак, мы распечатываем удерживаемое пространство, промываем его и снова начинаем с пункта 1. Пункт 1 реализуется кодом / ^ Record /! H
(5-я строка в команде). Это означает, что «если строка не начинается с Record
, добавить новую строку в удерживаемое пространство и добавить эту строку в удерживаемое пространство».
Пункт 2 может быть реализован с помощью кода
/ ^ Record / {x; s / \ n / / gp;}
, где x
меняет местами удержание и пространства шаблонов , Команда s
заменяет все \ n
на , а флаг
p
печатает пространство шаблонов. Использование x
также имеет то преимущество, что теперь пространство удержания содержит текущую строку Record
, так что мы можем начать еще один цикл точек 1 и 2.
Но у этого есть проблема. В данном примере есть две строки
a
b
перед первой строкой Record
.Мы не хотим заменять \ n
на в этих строках. Поскольку они не начинаются с
Запись
, согласно пункту 1, \ n
добавляется для удержания пробела, а затем эти строки добавляются. Итак, если первый символ удерживаемого пространства - \ n
, это означает, что Запись
ранее не встречалась, и мы не должны заменять \ n
на . Это делается с помощью команды
/^\n/{s/^\n//p;d}
Таким образом, вся команда становится
/^Record/{x;/^\n/{s/^\n//p;d};s/\n/ /gp};
Теперь вторая сложность заключается в том, что мы хотим соединить строки, даже если строка Record
не заканчивается Record
, но строкой PATTERN_END
. Мы хотим сделать то же самое, что и в пункте 2, даже если строка начинается с PATTERN_END
. Таким образом, команда становится
/^PATTERN_END/{x;/^\n/?s/^\n//p;d};s/\n/ /gp}
. Но с этим есть проблема. Как и в случае со строками Record
, строка PATTERN_END
теперь заканчивается в удерживаемом пространстве. Но мы знаем, что соединения строк после строки PATTERN_END
больше не будет. Итак, мы можем распечатать это. Итак, мы переносим строку PATTERN_END
в пространство шаблонов с помощью g
и печатаем ее с помощью p
. Таким образом, последняя команда становится
/^PATTERN_END/{x;/^\n/?s/^\n//p;d};s/\n/ /gp;g;p}
Следующая проблема связана со строками PATTERN_START
. В приведенном выше объяснении мы предполагали, что вначале удерживаемое пространство пусто. Но после PATTERN_END
в удерживаемом пространстве что-то есть. (Это просто строка PATTERN_END
).Когда мы начинаем новый цикл с PATTERN_START
, мы хотим очистить удерживаемое пространство.
Итак, что мы делаем, когда мы встречаем PATTERN_START
, меняем местами содержимое удержания и пространств шаблонов, очищаем пространство шаблонов и снова меняем местами. Это делает трюмное пространство чистым. Именно это и делает следующая команда:
/^PATTERN_START/{x;s/^.*$//;x}
Последний штрих состоит в том, что мы хотим выполнять всю эту возню только между строками PATTERN_START
и PATTERN_END
. Остальные мы их просто печатаем. Это делается с помощью команд
/^PATTERN_START/, /^PATTERN_END/{
----above commands go here----
};
/^PATTERN_START/, /^PATTERN_END/!p
Соберите все это вместе, и это даст последнюю команду:)
Я сделал три версии этого.
sed -e'/^PATTERN_START/!b' -e:n -eN \
-e'/\nPATTERN_END$/!bn' -eh\;s/// \
-e'x;s/\n[[:print:]]*$//;x' \
-e's/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-e'G;P;D' data
Он распечатывает весь файл после внесения изменений только в строки записи
, которые находятся между PATTERN_ {START, END}
.
sed -ne'/\n/P;:n' \
-e'/^PATTERN_[OS]/!D' -eN \
-e'/\nPATTERN_END$/!bn' -es/// \
-e'/^PATTERN_S/s/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-eG\;D ./data ###<gd data> haha
Этот печатает строки записи
в любом из ШАБЛОН _ {(START | OTHER), END}
, но применяет правки только к тем, которые встречаются между ШАБЛОН_ {НАЧАЛО, КОНЕЦ}
.
sed -ne'/\n/P;:n' \
-e'/^PATTERN_START/!D' -eN \
-e'/\nPATTERN_END$/!bn' -es/// \
-e's/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-eG\;D ./data
И только этот редактирует и только печатает строки записи
, которые встречаются между PATTERN_ {START, END}
.
Далее следует вывод каждого из них после прохождения через него входной выборки. Выходные образцы представлены в обратном порядке, от самого короткого до самого длинного.
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 2 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
PATTERN_END
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Data
Blabla
Blabla
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 2 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
PATTERN_END
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Blabla
Blabla