многострочный поиск sed-замена

#regex #sed #replace #multiline

#регулярное выражение #sed #заменить #многострочный

Вопрос:

Я получил код fortran с неприятным старым синтаксисом и хочу перенести на новый синтаксис. Моя sed команда

 sed -nr 'N;s/ns*dDs*//g' file 
  

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

 N                   append next line to pattern space; thus pattern space has two lines with n in between
s///g               usual search-replace
ns*dDs*        matches a newline followed by s*, a digit, a non-digit and a s* again
  

Исходный код выглядит следующим образом

    if(condition) then 
         call func1(v1, v2, v3, v4
     1              ,v5,v6,v7)
      else
         call func2(v1, v2, v3, v4
     1              ,v5,v6,v7)
      endif
call MPI_BCAST(num(1),1,MPI_DOUBLE_PRECISION
     1     ,masterid,comm,mpinfo)
21      format(' text',2x,f10.5)
  

и должен преобразоваться в целевой код

    if(condition) then 
         call func1(v1, v2, v3, v4,v5,v6,v7)
      else
         call func2(v1, v2, v3, v4,v5,v6,v7)
      endif
call MPI_BCAST(num(1),1,MPI_DOUBLE_PRECISION,masterid,comm,mpinfo)
21      format(' text',2x,f10.5)
  

Комментарии:

1. sed не поддерживает d или D .. s поддерживается GNU sed (не уверен в других реализациях)… кроме того, использование N (без дополнительных обходных команд) не позволит вам снова использовать эту строку. например, первые две строки сопряжены, поэтому вы никогда не сможете сопоставить 3-ю строку, которая вам нужна

Ответ №1:

Это может сработать для вас (GNU sed):

 sed -E ':a;N;s/ns*[0-9]s*([^0-9])/1/;ta;P;D' file
  

Пройдите по файлу, используя окно из 2 строк.

Если вторая строка начинается с некоторого или без пробелов, за которой следует цифра, за которой следует еще немного или без пробелов, за которой следует не цифра, замените это на не цифру и повторите. В противном случае выведите первую строку окна, затем удалите ее и повторите.

Комментарии:

1. Большое спасибо, это работает для меня! Но я немного смущен комментарием @Sundeep. Если sed с помощью команды N считывает файл в совместных строках 12, 34, 56, … вместо 12, 23, 34, … тогда я не понимаю, как приведенная выше команда может соответствовать разрыву строки в 23, 45, … Но, очевидно, это так ?!

2. Особенность D команды в том, что она не только удаляет до первой новой строки включительно в пространстве шаблонов, но и, если пространство шаблонов не является пустым, подавляет неявное чтение следующей строки и вместо этого переходит к первой команде в цикле sed. Таким образом, если в пространстве шаблонов было 2 строки, вторая становится первой.

3. Большое спасибо за подробное объяснение. Кажется, я понял!

Ответ №2:

Вот одно из возможных решений, perl которое работает для данного образца ввода:

 perl -0777 -pe 's/nh*dh*(?=,)//g'
  
  • -0777 вывод всего ввода в виде одной строки
  • nh*dh* сопоставьте символ новой строки, за которым следуют необязательные горизонтальные пробелы, за которыми следует символ цифры, за которым следуют необязательные горизонтальные пробелы
    • (?=,) совпадение только в том случае, если после такого совпадения есть символ запятой … в противном случае вам нужно будет указать, как НЕ совпадать 21 format(' text',2x,f10.5)

С GNU sed помощью, но мое понимание этих команд недостаточно хорошо, чтобы быть уверенным:

 sed -E 'N; s/ns*[0-9]s*,/,/; P; D'
  

Из руководства GNU sed:

P Распечатайте часть пространства шаблонов до первой новой строки.

D Если пространство шаблонов не содержит новой строки, запустите обычный новый цикл, как если бы была выдана команда d. В противном случае удалите текст в пространстве шаблонов до первой новой строки и перезапустите цикл с результирующим пространством шаблонов, не читая новую строку ввода.

Комментарии:

1. Ваше perl решение работает как шарм! Также ваш sed пример помог мне лучше понять механику, стоящую за этим. Но прежде чем углубляться perl , я стараюсь придерживаться sed , хотя я считаю, что perl намного проще во многих ситуациях.