#shell #sed
#shell #sed
Вопрос:
У меня есть текстовый файл с кучей исходных данных в нем. Я хочу извлечь раздел, который выглядит следующим образом:
scan 170110 fission power at tpd 220635.7 total power 107.127
Triad3PC-ScanPower v1.1.1.1 ld=2007-03-27
--------------------------------------------------------------------------------------------------
burnup type: measbu lattice split: on discontinuity factors: normal
--------------------------------------------------------------------------------------------------
y z a b c d e f g h j k l m n o p q r s t
34 ... 34
33 ... ... ... ... 33
32 ... ... ... ... ... 32
31 ... ... ... ... ... ... 31
30 ... ... ... ... ... ... ... 30
29 ... ... 1.280 1.393 1.232 ... ... ... 29
28 ... 0.597 1.390 ... 1.289 ... ... 28
27 ... 0.534 1.113 ... ... ... ... ... 27
26 ... ... 1.536 ... ... 0.706 ... ... ... 26
25 ... ... 1.207 1.128 ... ... 0.784 ... ... ... 25
24 ... ... ... 0.791 0.831 ... ... 0.733 ... 24
23 ... ... ... ... ... 1.069 ... ... 1.226 ... 23
22 ... ... ... 0.945 1.204 0.841 ... 0.687 ... 22
21 ... ... 1.352 ... 0.826 ... ... 1.134 1.383 ... 21
20 ... ... ... 2.246 ... 1.639 1.316 ... ... ... ... 20
19 ... 1.214 0.953 1.551 1.434 ... 1.229 1.048 ... ... 19
18 ... ... 1.256 0.824 ... ... 1.151 1.132 ... 1.552 ... 18
17 ... 0.701 1.311 ... 1.796 1.492 ... 2.365 1.101 ... 17
16 ... ... ... ... ... ... ... 0.970 1.042 1.467 ... 16
15 ... 1.234 ... 0.863 1.574 ... 1.050 0.923 ... ... 15
14 ... ... ... ... ... 1.642 1.571 ... 1.090 1.473 ... 14
13 ... ... 1.062 1.266 1.679 1.599 0.814 ... 1.506 ... 13
12 ... ... 1.430 ... ... ... ... ... ... ... ... 12
11 ... ... 0.978 ... 1.342 1.375 ... ... 1.318 ... 11
10 ... ... ... 0.963 ... 1.066 ... 0.783 0.480 10
9 ... 1.056 ... 1.291 ... ... 0.595 ... 0.145 ... 9
8 ... 1.230 ... ... 0.760 ... ... ... ... 8
7 ... ... 1.313 ... 0.907 ... 0.762 1.534 ... ... 7
6 ... ... 1.306 ... ... ... 1.292 ... ... 6
5 ... ... 1.012 0.818 ... ... ... ... 5
4 ... ... 1.252 ... 1.299 ... ... 4
3 ... ... ... 1.221 1.145 1.109 ... ... 3
2 ... ... ... ... ... ... ... 2
1 ... ... ... ... ... ... 1
0 ... ... ... ... ... 0
-1 ... ... ... ... -1
-2 ... -2
y z a b c d e f g h j k l m n o p q r s t
Моя команда sed:
sed -n '/fission power at tpd/,/ y z a b c/ { p; }' s.171012.so.power.info
Но sed в конечном итоге останавливается на первом экземпляре «y z a b c»
scan 171012 fission power at tpd 239802.1 total power 109.976
Triad3PC-ScanPower v1.1.1.1 ld=2007-03-27
--------------------------------------------------------------------------------------------------
burnup type: measbu lattice split: on discontinuity factors: normal
--------------------------------------------------------------------------------------------------
y z a b c d e f g h j k l m n o p q r s t
Я хочу, чтобы это остановилось во втором экземпляре внизу карты. Как мне сказать ему, чтобы он это сделал?
Ответ №1:
Использование sed
Попробуйте:
sed -n '/fission power at tpd/,/ y z a b c/{/ y z a b c/!p;}; / y z a b c/,/ y z a b c/ { p; }' s.171012.so.power.info
Чтобы упростить чтение, рассмотрим этот тестовый файл:
$ cat file.info
0
begin
1
head
2
3
head
4
Наша команда выдает этот вывод:
$ sed -n '/begin/,/head/{/head/!p;}; /head/,/head/p' file.info
begin
1
head
2
3
head
/begin/,/head/{/head/!p;}
печатает, начиная со строки, содержащей begin
до, но не включая первую строку, содержащую head
. /head/,/head/p
выводит данные из строки, содержащей head
, в следующую строку, содержащую head
.
Используя awk
Поскольку awk понимает арифметику, мы можем подсчитать количество вхождений строк, содержащих head
:
$ awk '/begin/{f=1}; f amp;amp; g<2; f amp;amp; /head/{g }' file.info
begin
1
head
2
3
head
Как и sed, awk обрабатывает входной файл по одной строке за раз. Здесь мы устанавливаем переменной awk f
значение 1 (true), когда достигаем строки, содержащей begin
. Если f
имеет значение true и g
меньше 2 (что означает менее двух вхождений head
), мы печатаем строку. Если f
имеет значение true и строка содержит head
, то мы увеличиваем g
.
Ответ №2:
Это может сработать для вас (GNU sed):
sed -n '/fission power at tpd/{:a;N;s/^.*y z a b c.*/amp;/M2p;Ta}' file
Используйте -n
опцию, чтобы отключить явную печать, т.е. действовать больше как grep.
Соберите строки в пространстве шаблонов от строки, содержащей fission power at tpd
, до другой строки, которая является вторым появлением строки, содержащей y z a b c
.
Примечание. Решение использует команду substitute в многострочном режиме и заменяет строку, которая совпадает сама по себе, если она встречается дважды. Команда подстановки при успешном выполнении устанавливает внутренний флаг, и если внутренний флаг не установлен, T
команда переводит поток программы в метку цикла. Таким образом, цикл создаст набор строк в пространстве шаблонов, напечатает их, когда будет найдено соответствие, и завершит цикл.
Ответ №3:
sed — лучший инструмент для использования, если вы выполняете s / old / new для отдельных строк. Это не то, что вы делаете, поэтому это плохой выбор, чтобы попытаться использовать для вашего приложения. Используя любой awk в любой оболочке в каждом окне UNIX:
awk '/fission power at tpd/{f=1} f{print; if ((/y z a b c/) amp;amp; ( c == 2)) exit}' file
Ответ №4:
Решение состоит в том, чтобы буферизировать строки, которые были прочитаны, либо до тех пор, пока не встретится шаблон остановки, в этом случае они должны быть выведены, либо до тех пор, пока не встретится конец файла, в этом случае буферизованные строки с момента последнего шаблона остановки отбрасываются. Следующее решение использует ~
в качестве разделителя между строками «буферизованными для вывода» и строками «буферизованными, но не подлежащими выводу» внутри пространства хранения:
start="fission power at tpd"
stop=" y z a b c"
sed -n '
# Filter the starting pattern
/'"$start"'/,${
# Add the separator to and hold the line
s/$/~/; h;
# foreach line
:again; n;
# We hold the line unconditionally, cause its after ~ separator
H;
# If its stopping pattern
/'"$stop"'/{
# Move the ~ separator to the end of hold buffer
x;s/~//;s/$/~/;x;
};
# If its end of file
${
# output from the hold buffe only the part before ~ separator
x;s/~.*//;p;q;
};
b again;
}' input
И oneliner:
sed -n '/'"$start"'/,${ s/$/~/; h; :again; n; H; /'"$stop"'/{x;s/~//;s/$/~/;x;}; ${ x;s/~.*//;p;q; }; b again; }' input
Вы могли бы сделать это без разделителя внутри пространства удержания, действуя в буфере шаблонов как в буфере «строки, которые не должны выводиться». Это приводит к тому, что stop
шаблон не совпадает со всей строкой, поэтому при написании шаблона нужно помнить, что не следует использовать ^
привязку, скорее n
или, возможно, больше вдоль (^|n)[^n]*some_pattern[^n]*$
.
sed -n '
# Filter from the starting pattern to the of file
/'"$start"'/,${
# Hold the starting line and read next line
h; n;
# Foreach line
:again; {
# If stopping pattern is found
/'"$stop"'/{
# Add everything up until now to hold space
H;
# Now we could be potentially at end of file here - check it before reading next line
$!{
# Clear pattern space, read next line and restart
n;
b again;
}
};
# If end of file
${
# print the content of hold space and quit
x;p;q;
};
# Add next line to pattern space
N;
}; b again;
}' input
И oneliner:
sed -n '/'"$start"'/,${h; n; :again; /'"$stop"'/{ H; $!{ n; b again; }; }; ${ x;p;q; }; N; b again; }' input