Как вставить новую строку перед шаблоном?

138

Как вставить новую строку перед шаблоном в строке?

Например, это вставит новую строку за шаблоном регулярных выражений.

sed 's/regex/&\n/g'

Как я могу сделать то же самое, но перед шаблоном?

Учитывая этот пример входного файла, шаблон для сопоставления является номер телефона.

some text (012)345-6789

Должен стать

some text
(012)345-6789
Деннис
источник
Просто повторяю ответ на Как вставить новую строку / перевод строки после строки, используя sed здесь:sed '/regex/G'
Adam Schmideg
1
@ NilsvonBarth, почему простой вопрос плохой вопрос?
Джош

Ответы:

177

Это работает в bashи zsh, протестировано на Linux и OS X:

sed 's/regexp/\'$'\n/g'

В общем случае для $последующего строкового литерала в одинарных кавычках bashвыполняется подстановка обратной косой черты в стиле C, например $'\t', перевод на литеральную вкладку. Кроме того, sed хочет, чтобы ваш литерал новой строки был экранирован с помощью обратной косой черты, отсюда и \ранее $. И, наконец, сам знак доллара не должен заключаться в кавычки, чтобы он интерпретировался оболочкой, поэтому мы закрываем кавычку перед $и затем снова открываем ее.

Изменить : как предложено в комментариях @ mklement0, это также работает:

sed $'s/regexp/\\\n/g'

Здесь происходит следующее: вся команда sed теперь является строкой в ​​стиле C, что означает, что обратный слеш, который sed должен быть размещен до того, как литерал новой строки теперь должен быть экранирован с помощью другого обратного слеша. Хотя это и более читабельно, в этом случае вы не сможете выполнять подстановку строк оболочки (без того, чтобы снова сделать это уродливым).

mojuba
источник
7
Это дает мне «неэкранированный символ новой строки внутри шаблона замены» в OSX.
Мэтт Гибсон
@Matt Gibson, это очень странно, потому что "неэкранированный перевод строки" дается только тогда, когда у вас есть реальный перевод строки без обратной косой черты в шаблоне подстановки. Мой код выше работает, на самом деле, и в некоторых других оболочках, например, zsh, ksh.
Моджуба
3
@Matt Gibson ... или, если вы забудете обратную косую черту перед '$' \ n в моем коде.
Моджуба
7
Как написано, эти выражения полностью заменяют регулярное выражение новой строкой, а не вставляют новую строку в середине существующей строки в соответствии с запросом. Вот как я использовал модифицированную форму этого ответа , чтобы вставить новую строку между двумя согласованными шаблонами: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Обратите внимание на две одинарные кавычки после \ n. Первый закрывает $раздел " ", так что оставшаяся часть строки не затрагивается им. Без этих кавычек, \ 2 был проигнорирован.
Дэвид Раветти
12
Другой вариант заключается в использовании одной строки в кавычках ANSI C :, sed $'s/regexp/\\\n/g'которая улучшает читабельность - единственное предостережение в том, что вам необходимо удвоить все буквенные \ символы.
mklement0
43

Некоторые другие ответы не работали для моей версии sed. Переключение позиций &и \nсделал работу.

sed 's/regexp/\n&/g' 

Изменить: Это не похоже на OS X, если вы не установите gnu-sed.

Деннис
источник
9
Я не уверен, что это работает во всех версиях sed. Я попробовал это на своем Mac, и \ n просто получает вывод как 'n'
Тодд Гамблин,
3
Потратил 15 минут на Mac на моей работе, прежде чем читать ваш ответ. Go Apple!
Rick77
1
Для тех, кто использует доморощенный: brew install gnu-sedзатемgsed 's/regexp/\n&/g'
aaaaaa
1
... а затемecho 'alias sed=gsed' >> ~/.bashrc
Проксимо
36

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

$ sed 's/regexp/\
&/'

Пример:

$ echo foo | sed 's/.*/\
&/'

foo

Смотрите здесь для деталей. Если вы хотите что-то немного менее неловкое, вы можете попробовать использовать perl -peс группами совпадений вместо sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 относится к первой сопоставленной группе в регулярном выражении, где группы заключены в скобки.

Тодд Гамблин
источник
Почему вы говорите, что не можете добавлять новые строки? Вы можете просто сделать sed 's / regexp / & \ n / g' Вот и все
Андрес
2
Это наименее хакерская вещь, которую вы можете сделать на Mac для вставки новой строки (\ n не работает на Mac)
Pylinux,
Версия Perl может быть изменена для редактирования на местеperl -pi -e 's/(.*)/\n$1/' foo
Eponymous,
2
@Andres: (В основном) реализации Sed только для POSIX-функций, такие как версия BSD, которая также поставляется с OS X, не поддерживают escape-последовательности управляющих символов в части подстановки sвызова функции (в отличие от реализации Sed GNU , которая это делает) , Приведенный выше ответ работает с обеими реализациями; обзор всех отличий см. здесь .
mklement0
29

На моем Mac следующее вставляет один 'n' вместо новой строки:

sed 's/regexp/\n&/g'

Это заменяет на новую строку:

sed "s/regexp/\\`echo -e '\n\r'`/g"
Рев Скуллестад
источник
Я делал inline edit sed -i '' -e ...и у меня были проблемы с ^Mзаписью каретки M (ctrl + m) в файл. В итоге я использовал Perl с теми же параметрами.
Стив Таубер
2
Обратите внимание на тот факт, что второй код вставляет специальный код новой строки LF CR (обратная сторона MS-DOS CR LF)! И Unix-подобные ОС, и Mac OS X используют только LF ( \n).
Пабук
Что-то еще в моём выражении sed вызывало столько несчастий (несмотря на то, что оно работало нормально без echo...символа и новой строки), что я просто сделал это в vim.
Ахмед Фасих
1
Или просто: sed "s/regexp/`echo`/g"- это даст один LF вместо LF-CR
mojuba
2
@mojuba: No: `echo`приведет к пустой строке , потому что подстановки команд неизменно обрезают все завершающие символы новой строки. Невозможно использовать подстановку команд для прямой вставки одной новой строки (и вставка \n\r- то есть дополнительный CR - ужасная идея).
mklement0
15
echo one,two,three | sed 's/,/\
/g'
Вивек
источник
1
+1 работал отлично и довольно прямо вперед / легко запоминается
gMale
2
Этот ответ на самом деле является решением sed, а не решением bash . Все, что использует подобные конструкции, $'\n'полагается на оболочку для генерации новой строки. Такие решения не могут быть портативными. Этот. Конечно, это также дубликат второго примера в ответе tgamblin от 2009 года.
ghoti
10

В этом случае я не использую sed. Я использую tr.

cat Somefile |tr ',' '\012' 

Это берет запятую и заменяет ее возвратом каретки.

user1612632
источник
1
Я нашел это также работает: cat Somefile | tr ',' '\n'YMMV
LS
9

Вы можете использовать perl one-liners так же, как и с sed, с преимуществом полной поддержки регулярных выражений perl (что гораздо мощнее, чем то, что вы получаете с sed). На * nix платформах также очень мало изменений - perl обычно perl. Таким образом, вы можете перестать беспокоиться о том, как заставить версию вашей конкретной системы sed делать то, что вы хотите.

В этом случае вы можете сделать

perl -pe 's/(regex)/\n$1/'

-pe помещает perl в цикл «выполнить и напечатать», очень похожий на обычный режим работы sed.

' цитирует все остальное, чтобы оболочка не мешала

()окружающее регулярное выражение является оператором группировки. $1на правой стороне замены печатает то, что было найдено в этих скобках.

Наконец, \nэто новая строка.

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

\(\d\d\d\)\d\d\d-\d\d\d\d

\(или \)соответствует буквальному пареню и \dсоответствует цифре.

Лучше:

\(\d{3}\)\d{3}-\d{4}

Я полагаю, вы можете выяснить, что делают числа в фигурных скобках.

Кроме того, вы можете использовать разделители, отличные от / для вашего регулярного выражения. Так что если вам нужно соответствовать / вам не нужно избегать его. Любое из нижеприведенного равнозначно регулярному выражению в начале моего ответа. Теоретически вы можете заменить любой символ на стандартное.

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Пара заключительных мыслей.

использование -neвместо -peдействует аналогично, но в конце автоматически не печатается. Это может быть удобно, если вы хотите печатать самостоятельно. Например, вот что такое grep ( m/foobar/как регулярное выражение):

perl -ne 'if (m/foobar/) {print}'

Если вам трудно работать с новыми строками, и вы хотите, чтобы это было волшебным образом для вас, добавьте -l. Не полезно для ОП, который работал с новыми строками.

Дополнительный совет - если у вас установлен пакет pcre, он поставляется вместе с pcregrepрегулярными регулярными выражениями, совместимыми с perl.

Дэн Приттс
источник
4

Хм, только что вышедшие из новой строки, похоже, работают в более поздних версиях sed(у меня GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar
gatoatigrado
источник
1
Как уже упоминалось, это работает с различными версиями GNU sed, но не с sed, включенным в macOS.
LS
4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

отлично работал на Эль Captitan при ()поддержке

Quanlong
источник
Это отлично сработало, и вы даже дали полную команду для тестирования и экстраполяции, чтобы специализироваться для своих целей. Хорошая работа!
jxramos
3

Чтобы вставить новую строку в выходной поток в Linux, я использовал:

sed -i "s/def/abc\\\ndef/" file1

Где file1был:

def

Перед заменой сид на месте, и:

abc
def

После седа на месте замены. Пожалуйста, обратите внимание на использование \\\n. Если у шаблонов есть "внутренняя часть, избегайте использования \".

Картик
источник
Для меня код выше не работает. sedвставляет \nвместо LF, потому что он получает \\nпараметр из оболочки. --- Этот код работает: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(CentOS выпуск 6.4 / Ubuntu 12.04.3).
Пабук
2

В sed вы можете ссылаться на группы в вашем шаблоне с помощью "\ 1", "\ 2", ...., так что если вы ищете шаблон "PATTERN", и вы хотите вставить перед ним "BEFORE" Вы можете использовать, без побега

sed 's/(PATTERN)/BEFORE\1/g'

т.е.

  sed 's/\(PATTERN\)/BEFORE\1/g'
Стив Б.
источник
Только что сделал: содержание тестового файла = "ABC ABC ABC". Запустил тестовый файл sed 's / \ (ABC \) / \ n \ 1 / g', получил новые строки. Поэкспериментируйте с escape-символами, попробуйте добавить по одной вещи за раз в ваш шаблон, например, убедитесь, что вы соответствуете шаблон, затем проверьте соответствие группы, затем добавьте проверку новой строки
Стив Б.
Я просто попробовал именно это и получил «nABC nABC nABC». Вы используете какую-то другую версию sed?
Тодд Гамблин
побег оболочки, вероятно, мешает попыткам tgamblin. положив полные аргументы sed в одинарные кавычки, как это сделал Стив Б., это нужно исправить. Возможно, что разные версии sed не понимают \ n для новой строки.
Дэн Притц
2

Вы также можете сделать это с помощью awk, используя -vшаблон:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

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

Смотрите базовый пример:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Обратите внимание, что это повлияет на все шаблоны в строке:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead
Федорки "ТАК прекратить вредить"
источник
1
что 1 делает в этом?
whatahitson
1
@whatahitson 1используется в Awk для краткости {print $0}. Причина в том, что любое условие, которое оценивается как True, запускает действие Awk по умолчанию, которое состоит в печати текущей записи.
Федорки 'ТАК прекрати вредить'
1

Это работает в MAC для меня

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Дону ли его идеальный ...

Сэм
источник
1

Прочитав все ответы на этот вопрос, мне все еще потребовалось много попыток получить правильный синтаксис для следующего примера сценария:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

Скрипт добавляет новую \nстроку, за которой следует другая строка текста, в конец файла с помощью одной sedкоманды.

Ксавье
источник
1
sed -e 's/regexp/\0\n/g'

\ 0 - это ноль, поэтому ваше выражение заменяется на ноль (ничего), а затем ...
\ n - это новая строка

Некоторые версии Unix не работают, но я думаю, что это решение вашей проблемы.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow
tmow
источник
0

В vi в Red Hat мне удалось вставить возврат каретки, используя только символ \ r. Я считаю, что это внутренне выполняет 'ex' вместо 'sed', но это похоже, и vi может быть другим способом массового редактирования, такого как исправления кода. Например. Я окружаю поисковый запрос выражением if, которое настаивает на возврате каретки после фигурных скобок:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Обратите внимание, что я также вставил несколько вкладок, чтобы все выровнялось лучше.

Роберт Кейси
источник