Я пытаюсь заменить шаблон между строками файла.
В частности, я хотел бы заменить ,\n &
на , &\n
в больших и нескольких файлах. Это фактически перемещает символ & на предыдущую строку. Это очень просто с CTR+H, но мне было сложно с sed.
Итак, исходный файл имеет следующий вид:
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
Желаемая форма вывода:
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
Следуя предыдущим ответам на вопросы о stackoverflow, я попытался преобразовать его с помощью следующих команд:
sed ':a;N;$!ba;s/,\n &/&\n /g' file1.txt > file2.txt
sed -i -e '$!N;/&/b1' -e 'P;D' -e:1 -e 's/\n[[:space:]]*/ /' file2.txt
но они терпят неудачу, если в файле присутствует символ "#".
Есть ли способ заменить совпадающий шаблон проще, скажем:
sed -i 's/,\n &/, &\n /g' file
Заранее спасибо!
🤔 А знаете ли вы, что...
Bash поддерживает множество операторов для выполнения арифметических операций.
Использование sed
$ sed ':a;N;s/\n \+\(&\) \(.*\)/ \1\n \2/;ba' input_file
A +, &
B -, &
C ), &
D +, &
E (, &
F *,
# & G -, &
H +, &
I (, &
J +,
Если вы используете GNU sed
и ваш файл не содержит символов NUL (код ASCII 0), вы можете использовать его опцию -z
для обработки всего файла как одной строки и многострочный режим команды замены (флаг m
). Флаг m
не является абсолютно необходимым, но он немного упрощает (.
и классы символов не соответствуют символам новой строки):
$ sed -Ez ':a;s/((\`|\n)[^#]*,)((\n.*#.*)*)(\n[[:blank:]]*)&/\1 \&\3\5 /gm;ta' file
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
Это соответствует вашей текстовой спецификации и желаемому результату для примера, который вы показываете. Но это немного сложно. Вместо обработки строк, которые заканчиваются символом новой строки, он обрабатывает подстроки, которые начинаются с символа новой строки (или начала файла) и заканчиваются перед следующим символом новой строки. Назовем их "куски".
Мы ищем последовательность чанков в виде AB*C
, где:
A
— фрагмент (возможно, первый), не содержащий #
. Он соответствует (\<backtick>|\n)[^#]*,
, что означает начало файла или новую строку, за которым следует любое количество символов, кроме новой строки и #
, за которыми следует запятая.B*
— любое количество (включая отсутствие) чанков, содержащих #
. Ему соответствует \n.*#.*
, что означает новую строку, за которой следует любое количество символов, кроме новой строки, за которой следует #
и любое количество символов, кроме новой строки.C
— фрагмент, начинающийся с новой строки, за которым следуют пробелы и &
. Он соответствует \n[[:blank:]]*&
, что означает новую строку, за которой следует любое количество пробелов и &
.Если мы находим такую AB*C
последовательность, мы добавляем пробел и &
в конце A
, мы не меняем B*
и заменяем первый &
в C
пробелом. И повторяем до тех пор, пока такой последовательности не будет найдено.
Примечание: если за запятыми могут следовать пробелы перед новой строкой, мы должны это учитывать. Если вы хотите сохранить их:
$ sed -Ez ':a;s/((\`|\n)[^#]*,[[:blank:]]*)((\n.*#.*)*)(\n[[:blank:]]*)&/\1 \&\3\5 /gm;ta' file
Еще:
$ sed -Ez ':a;s/((\`|\n)[^#]*,)[[:blank:]]*((\n.*#.*)*)(\n[[:blank:]]*)&/\1 \&\3\5 /gm;ta' file
Предполагая, что линия
# & G -,
является закомментированной строкой, которая может быть раскомментирована позже, может иметь смысл обрабатывать &
и в этой строке. Не зная цели данных, это может быть полезно, а может и нет.
С ГНУ Awk команда
awk 'BEGIN { RS = ",";ORS = "" } { printf "%s%s", ORS, gensub(/(\n[ \t#]*)&/, " \\&\\1 ",1); ORS=RS }' inputfile
повернет вход
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
в
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# G -, &
H +, &
I (, &
J +,
K ?,
Этот скрипт будет работать правильно, только если последняя строка заканчивается новой строкой или любой другой символ следует за ,
.
Объяснение:
RS = ","
устанавливает запятую в качестве разделителя записи вместо новой строки для ввода.ORS = ""
устанавливает разделитель выходных записей на пустую строку перед первой записью.fprintf "%s%s", ORS, gensub(...)
до Добавляет разделитель записей вместо добавления.gensub
Специальная функция подстановки GNU, которая позволяет использовать обратные ссылки на соответствующие группы./(\n[ \t#]*)&/
шаблон поиска: круглые скобки определяют группу (1), которая состоит из новой строки \n
, за которой следует любая последовательность пробелов, символов табуляции или комментариев [ \t#]*
. За группой следует персонаж &
." \\&\\1 "
замена: пробел, за которым следует &
, за которым следует захваченная группа (1) (\\1
) и дополнительный пробел для замены удаленного &
. (\\&
необходим для получения буквального символа &
вместо вставки всего совпадения.)ORS=RS
устанавливает разделитель выходных записей на ,
после первой строки. (фактически после каждого ros) ставить запятую перед 2-й и последующими записями. Это гарантирует, что последняя запись, которая должна быть новой строкой, не получит завершающую ,
.Приведенная ниже версия скрипта GNU Awk
будет работать, как ожидается, Только, если последняя строка входного файла нет завершается символом новой строки.
Это создаст дополнительную строку с ,
, потому что последняя запись, содержащая новую строку, будет завершена разделителем выходных записей ,
.
awk 'BEGIN { RS=ORS = "," } { print gensub(/(\n[ \t#]*)&/, " \\&\\1 ",1) }' inputfile
Если входной файл заканчивается новой строкой, вывод будет
...
I (, &
J +,
K ?,
,
без новой строки после последнего ,
.
Использование sed
sed -En 'H;${g;s/^\n//;s/((\n *#.*)*)\n +&(.*)/ \&\1\n \3/gmp}' file
Объяснение
-E
Включить расширенное регулярное выражение-n
Предотвратить печать sed по умолчаниюH
Добавить для удержания места${
Когда в концеg
Перезаписать то, что находится в пространстве хранения, в пространство шаблонаs/^\n//;
удалить начальную новую строку из пробелаs/
Начать замену((\n *#.*)*)
Группа захвата 1, при необходимости повторите сопоставление новой строки и #, за которым следует остальная часть строки\n +&(.*)
Сопоставьте новую строку и 1+ пробелов, затем сопоставьте &
и захватите оставшуюся часть строки в группе 3/
Замените после этого \&\1\n \3
Шаблон замены с группами захвата и экранированными &
/
Конец заменыgmp
Выход
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,%
См. баш демо.