Як вставити нову лінію перед візерунком?


138

Як вставити новий рядок перед шаблоном у межах рядка?

Наприклад, для цього буде вставлено новий рядок за схемою регулярних виразів.

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

Як я можу зробити те саме, але перед шаблоном?

З огляду на цей зразок вхідного файлу, шаблон, на який потрібно відповідати, - це номер телефону.

some text (012)345-6789

Повинен стати

some text
(012)345-6789


1
@NilsvonBarth, чому пряме запитання - це погане питання?
Джош

Відповіді:


177

Це працює в bashі zshперевірено на Linux та OS X:

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

Взагалі, за $яким слідує рядковий літерал в одинарних лапках, bashвиконує підстановку зворотної косої лінії в стилі С, наприклад $'\t', переводиться на вкладку буквальної. Плюс, sed хоче, щоб ваш новий рядок був уникнути зворотним нахилом, отже, і \раніше $. І нарешті, сам знак долара не повинен котируватися так, щоб він тлумачився оболонкою, тому ми закриваємо цитату перед, $а потім відкриваємо її знову.

Редагувати : Як запропоновано в коментарях @ mklement0, це також працює:

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

Тут відбувається: вся команда sed тепер є рядком у стилі C, що означає зворотну косу рису, яку потрібно розмістити sed, перш ніж новий літеральний рядок повинен бути уникнути з іншим нахилом. Хоча легше читати, у цьому випадку ви не зможете робити підстановку рядкових оболонок (не роблячи її знову некрасивою.)


7
Це дає мені "нерозроблений новий рядок всередині замінника" на OSX.
Метт Гібсон

@Matt Gibson, що дуже дивно, тому що "нерозміщений новий рядок" надається лише тоді, коли у вас є справжня нова лінія без зворотної косої риси в рамках схеми заміни. Мій код вище працює, власне, і в інших оболонках, наприклад, zsh, ksh.
mojuba

3
@Matt Gibson ... або якщо ви забудете зворотну косу рису перед "$" \ n в коді.
mojuba

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, перш ніж прочитати вашу відповідь. Іди 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
однойменний

2
@Andres: (Здебільшого) реалізація Sed-лише функцій POSIX, така як версія BSD, яка також постачається з OS X, не підтримує послідовності втечі контрольних символів у частині заміни sвиклику функції (на відміну від реалізації GNU Sed, що робить) . Наведена відповідь працює з обома реалізаціями; огляд усіх відмінностей дивіться тут .
mklement0

29

На моєму mac наступне вставляє один 'n' замість нового рядка:

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

Це замінює новий рядок:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Я робив вбудовуване редагування sed -i '' -e ...і мав проблеми з записом ^MM (ctrl + m), записаного у файл. Я в кінцевому підсумку використовував perl з тими ж парамами.
Стів Таубер

2
Будь ласка, пам’ятайте про те, що другий код вставляє спеціальний код нового рядка LF CR (зворотний бік MS-DOS CR LF)! Як Unix-подібні ОС, так і Mac OS X використовують просто LF ( \n).
pabouk

Щось інше в моєму виразі sed викликало стільки нещастя (незважаючи на те, що він справно працює без echo...нового та нового рядка), що я щойно це зробив in vim.
Ахмед Фасіх

1
Або просто: sed "s/regexp/`echo`/g"- це дасть єдиний НЧ замість LF-CR
mojuba

2
@mojuba: Ні: `echo`призведе до порожнього рядка , оскільки підстановки команд незмінно обрізають усі проміжні рядки. Немає можливості використовувати заміну команди, щоб безпосередньо вставити один новий рядок (а вставка \n\r- тобто додатковий CR - це жахлива ідея).
mklement0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 працював ідеально і досить просто-вперед / легко запам'ятовується
gMale

2
Відповідь на це питання насправді є СЄПН рішення , а не Баш рішення. Все, що використовує такі конструкції $'\n', покладається на оболонку для створення нового рядка. Такі рішення можуть бути не портативними. Цей є. Звичайно, це також дублікат другого прикладу у відповіді tgamblin від 2009 р.
ghoti

10

У цьому випадку я не використовую sed. Я використовую tr.

cat Somefile |tr ',' '\012' 

Це бере кома і замінює її поверненням каретки.


1
Я виявив, що це також працює: cat Somefile | tr ',' '\n'YMMV
LS

9

Ви можете використовувати perl-вкладиші так, як ви робите з sed, з перевагою повної підтримки регулярного вираження perl (що набагато потужніше, ніж те, що ви отримуєте з sed). Існує також дуже мало варіацій для * nix платформ - perl, як правило, perl. Таким чином, ви можете перестати турбуватися про те, як змусити версію вашої конкретної системи sed робити те, що ви хочете.

У цьому випадку можна зробити

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

-pe додає perl у цикл "Execute and print", як звичайний режим роботи 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дії аналогічно, але не друкується автоматично наприкінці. Це може бути зручно, якщо ви хочете друкувати самостійно. Наприклад, ось подібний греп ( m/foobar/є збігом регулярних виразів):

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

Якщо ви вважаєте, що справу з новинками викликає занепокоєння, і ви хочете, щоб це було магічним чином для вас, додайте -l. Однак це не корисно для ОП, які працювали з новими лініями.

Порада про бонуси - якщо у вас встановлений пакет pcre, він постачається разом із ним pcregrep, який використовує повні сумісні пергекси.


4

Хм, щойно вийшли нові рядки, здається, працюють у новіших версіях sed(у мене є GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Як вже згадувалося, це працює з різними версіями GNU sed, але не sed, що входить до системи macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

добре працював на El Captitan за ()підтримки


Це спрацювало чудово, і ви навіть даєте повну команду провести тестування та екстраполяцію, щоб спеціалізуватися на власні цілі. Хороша робота!
jxramos

3

Щоб вставити новий рядок у вихідний потік на Linux, я використав:

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

Де file1було:

def

Перед заміною sed на місці та:

abc
def

Після заміни sed на місці. Зверніть увагу на використання \\\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", і ви хочете вставити перед ним "ДО" , ви можете використовувати, без втечі

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

тобто

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

Щойно зробив: testfile content = "ABC ABC ABC". Перевірив тест-файл "sed 's / \ (ABC \) / \ n \ 1 / g", отримав нові рядки. Експериментуй із втечами, спробуй додати по одному шаблону за раз, наприклад, переконайся, що ти відповідає шаблон, а потім перевірити відповідність групи, а потім додати перевірку нового рядка.
Steve B.

Я просто спробував саме це і отримав "nABC nABC nABC". Чи використовуєте ви якусь іншу версію sed?
Todd Gamblin

втеча снаряда, ймовірно, стає на шляху спроб тагбліна. розміщення повних аргументів sed в одиничних цитатах, як це робив Стів Б, має це виправити. Можливо, якщо різні версії sed не розуміють \ n для нового рядка.
Dan Pritts

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}. Причина полягає в тому, що будь-яка умова, яка оцінюється як Істинна, запускає дію за замовчуванням Awk, яка полягає у друкуванні поточного запису.
fedorqui 'ТАК перестаньте шкодити'

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 - це нуль, тому вираз замінюється на null (нічого), а потім ...
\ n - новий рядок

На деяких ароматах Unix не працює, але я думаю, що це рішення вашої проблеми.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

У vi на Red Hat мені вдалося вставити повернення каретки, використовуючи лише символ \ r. Я вважаю, що це внутрішньо виконує "ex" замість "sed", але це схоже, і vi може бути іншим способом зробити масові редагування, такі як патчі коду. Наприклад. Я оточую пошуковий термін із твердженням if, яке наполягає на поверненні перевезення після дужок:

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

Зауважте, що мені також доводилося вставляти кілька вкладок, щоб зробити речі вирівняними.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.