У меня есть файл с несколькими строками, и я хочу объединить строки, если они обе соответствуют определенному шаблону.
Я знаю, что могу найти строки, соответствующие шаблону, и получить следующую строку с помощью:
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. Данные передаются в given
Raku в виде 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\nBush
4 строк в 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