Как соединить две строки в файле, если они обе соответствуют шаблону?

Как соединить две строки в файле, если они обе соответствуют шаблону?

У меня есть файл с несколькими строками, и я хочу объединить строки, если они обе соответствуют определенному шаблону.

Я знаю, что могу найти строки, соответствующие шаблону, и получить следующую строку с помощью:

grep -E -A1 'Pattern' filename

Но как мне проверить, соответствует ли следующая строка шаблону, и как мне соединить их?

Например, у меня есть такой файл:

Hello
i
am
John
Smith

Примером может служить следующий шаблон:

'^[A-Z][a-z]+'

Поэтому в этом случае я хотел бы объединить строки, если они обе начинаются с заглавных букв.

Результат, которого я хотел бы достичь, был бы следующим:

Hello
i
am 
John Smith

решение1

/^[A-Z][a-z]+/{
  :a
  N
  /\n[A-Z][a-z]+/{
    s/\n/ /
    b a
  }
}

Сохраните его как join.sedи выполните: sed -Ef join.sed file.

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

Для GNU Sed вы можете свернуть его в одну строку:

sed -E '/^[A-Z][a-z]+/{:a;N;/\n[A-Z][a-z]+/{s/\n/ /;b a}}' file

В качестве альтернативы можно использовать скрипт Awk, join.awkдля которого шаблон должен быть задан следующим образом p:

{
    if($0~p)c+=1
    else c=0
    printf "%s%s", (c>1 ? " " : ors), $0
    ors=ORS
}
END{print ""}

Выполнить: awk -f join.awk p='^[A-Z][a-z]+' file.

решение2

Используйте sedнулевой символ в качестве разделителя ( -z):

$ sed -z 's/\([A-Z][a-z]\+\)\n\([A-Z][a-z]\+\)/\1 \2/'
Hello
i
am
John Smith

решение3

Использование Raku (ранее известного как Perl_6)

raku -e 'given lines.join("\n") { S/ $<first>=[<upper><lower>+] \n $<last>=[<upper><lower>+] /$<first> $<last>/.put};'

Пример ввода:

Hello
i
am
John
Smith
goodbye

Пример вывода:

Hello
i
am
John Smith
goodbye

Выше представлено решение, закодированное на языке Raku, члене семейства языков Perl. Данные передаются в givenRaku в виде lines, но поскольку процедура Raku linesавтоматически поглощает ввод, данные -ed joinс переводами строк. Хотя это может показаться немного запутанным, преимущество в том, что linesпроцедура Raku считывает данные лениво, то есть код вышедолжно бытьэффективное использование памяти.

Raku реализует S///"неразрушающий" оператор, который похож (если не идентичен) знакомому s///оператору (в Raku он тоже есть). У оператора capital- Sесть одно преимущество, заключающееся в том, что он«оставляет исходную строку нетронутой и возвращает результирующую строку вместо $/ (переменной сопоставления)».

В соответствующей (левой) половине оператора S///,именованные захватыиспользуются. Механизм регулярных выражений сначала ищет [<upper><lower>+]и назначает его именованному захвату $<first>, затем ищет \n(новую строку) и, наконец, ищет еще один [<upper><lower>+], на этот раз назначая его именованному захвату $<last>. В завершение, в подстановочной (правой) половине оператора S///два именованных захвата $<first> $<last>используются для замены совпадения с левой стороны, хотяспространство ибезновая \nстрока между ними.

Альтернативный способ сделать то же самое приведен ниже. Код пропускает именованные захваты, вместо этого используя <(\n)>для удаления всего из объекта соответствия, кроме того, что находится внутри <(…)>маркеров захвата. Затем в замене \n заменяется на пробел:

raku -e 'put S/ [<upper><lower>+] <(\n)> [<upper><lower>+] / / given lines.join("\n");'  

[Обратите внимание, что код выше свернёт только что-то вроде George\nHerbert\nWalker\nBush4 строк в 3 ( George Herbert\nWalker\nBush). Если вы хотите, чтобы все последовательные вхождения строки [<upper><lower>+]возвращались в одной строке, пожалуйста, не стесняйтесь задавать этот вопрос].

https://docs.raku.org/language/regexes#S///_неразрушающая_подстановка
https://docs.raku.org/language/regexes#index-entry-regex__Именованные_захваты-Именованные_захваты
https://raku.org

Связанный контент