Get sed извлекает текст между двумя точками, но останавливается при втором появлении строки

#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
  

Протестировано на repl.