Вставьте перевод строки в sed (Mac OS X) - PullRequest
39 голосов
/ 24 мая 2011

Как вставить новую строку в запасную часть sed?

Этот код не работает:

sed "s/\(1234\)/\n\1/g" input.txt > output.txt

где input.txt:

test1234foo123bar1234

и output.txt должен быть:

test
1234foo123bar
1234

но вот я получаю это:

testn1234foo123barn1234

Примечание:

Этот вопрос конкретно касается версии «sed» для Mac OS X, и сообщество отметило, что она ведет себя иначе, чем, скажем, версии Linux.

Ответы [ 9 ]

60 голосов
/ 24 мая 2011

Ваша версия sed, по-видимому, не поддерживает \n в RHS (правая часть замещения).Чтобы выбрать одно из возможных решений, прочитайте FAQ по SED , которое ведет Эрик Пимент.Я предлагаю попробовать сначала вставить буквальный символ новой строки.

Ниже приводится цитата из него.


4.1.Как вставить новую строку в RHS замены?

Несколько версий sed позволяют вводить \n непосредственно в RHS, который затем преобразуется в новую строку при выводе: ssed,gsed302a +, gsed103 (с переключателем -x), sed15 +, sedmod и UnixDOS sed.Самое простое решение - использовать одну из этих версий.

Для других версий sed попробуйте одну из следующих:

(a) Если вы набираете скрипт sed изв оболочке Bourne используйте одну обратную косую черту \, если в сценарии используются «одинарные кавычки», или две обратные косые черты \\, если для сценария требуются «двойные кавычки».В приведенном ниже примере обратите внимание, что оболочка > во 2-й строке генерируется оболочкой, чтобы запросить у пользователя дополнительные данные.Пользователь вводит косую черту, одинарные кавычки и затем ENTER, чтобы завершить команду:

 [sh-prompt]$ echo twolines | sed 's/two/& new\
 >/'
 two new
 lines
 [bash-prompt]$

(b) Использовать файл сценария с одной обратной косой чертой \ в сценарии,сразу же после новой строки.Это вставит новую строку в часть «заменить».Пример:

 sed -f newline.sed files

 # newline.sed
 s/twolines/two new\
 lines/g

Некоторые версии sed могут не нуждаться в обратной косой черте.Если это так, удалите его.

(c) Вставьте неиспользуемый символ и направьте вывод через tr:

 echo twolines | sed 's/two/& new=/' | tr "=" "\n"   # produces
 two new
 lines

(d) Используйте команду G:

G добавляет новую строку, а также содержимое области удержания в конец области шаблона.Если пространство удержания пусто, новая строка добавляется в любом случае.Новая строка сохраняется в пространстве шаблонов как \n, где ее можно найти, сгруппировав \(...\) и переместив ее в RHS.Таким образом, для изменения примера «twolines», использовавшегося ранее, будет работать следующий скрипт:

 sed '/twolines/{G;s/\(two\)\(lines\)\(\n\)/\1\3\2/;}'

(e) Вставка полных строк, без разбивки строк:

Если вы не меняете строки, а вставляете только целые строки до или после шаблона, процедура будет намного проще.Используйте команду i (вставка) или a (добавление), внося изменения с помощью внешнего скрипта.Чтобы вставить This line is new ДО каждой строки, соответствующей регулярному выражению:

 /RE/i This line is new               # HHsed, sedmod, gsed 3.02a
 /RE/{x;s/$/This line is new/;G;}     # other seds

Два приведенных выше примера предназначены для ввода однострочных команд с консоли.Если используется сценарий sed, i\, за которым сразу следует буквальный перевод строки, будет работать на всех версиях sed.Кроме того, команда s/$/This line is new/ будет работать только в том случае, если пространство удержания уже пусто (что по умолчанию).

Для добавления This line is new ПОСЛЕ каждой строки, соответствующей регулярному выражению:

 /RE/a This line is new               # HHsed, sedmod, gsed 3.02a
 /RE/{G;s/$/This line is new/;}       # other seds

Чтобы добавить 2 пустые строки после каждой строки, соответствующей регулярному выражению:

 /RE/{G;G;}                    # assumes the hold space is empty

Чтобы заменить каждую строку, соответствующую регулярному выражению, на 5 пустых строк:

 /RE/{s/.*//;G;G;G;G;}         # assumes the hold space is empty

(f) Если возможно, используйте команду y///:

В некоторых версиях sed для Unix (не в GNU sed!), Хотя команда s/// не будет принимать \n в RHS, y/// команда делает.Если ваш Unix sed поддерживает это, можно вставить новую строку после aaa таким образом (который не переносится в GNU sed или другие seds):

 s/aaa/&~/; y/~/\n/;    # assuming no other '~' is on the line!
19 голосов
/ 19 июня 2014

Это однострочное решение , которое работает с любым POSIX-совместимым sed (включая версию FreeBSD для macOS), при условии, что ваша оболочка bash или ksh или zsh:

sed 's/\(1234\)/\'$'\n''\1/g' <<<'test1234foo123bar1234'

Обратите внимание, что вы могли бы использовать одну строку ANSI C в кавычках в качестве целого sed сценария, sed $'...' <<<, но это потребует \ -экранирования всех \ экземпляров (удвоение их), что довольно громоздко и затрудняет читабельность, о чем свидетельствует ответ @ tovk ).

  • $'\n' представляет новую строку и является экземпляром цитирование ANSI C , которое позволяет создавать строки с управляющими символами escape-последовательностей.
  • Вышеуказанные сращивания строки в кавычках ANSI C в sed сценарий следующим образом:
    • Сценарий просто разбит на 2 строки в одинарных кавычках, а строка ANSI C в кавычках застряла между двумя половинами :
    • 's/\(1234\)/\' является первой половиной - обратите внимание, что оканчивается на \, чтобы экранировать символ новой строки, который будет вставлен как следующий символ. (это экранирование необходимо, чтобы отметить новую строку как часть строки замены, а не интерпретируется как конец команды).
    • $'\n' - это представление символа новой строки в кавычках ANSI, , которое расширяется до фактического перевода строки перед передачей сценария в sed.
    • '\1/g' - вторая половина.

Обратите внимание, что это решение работает аналогично для других управляющих символов , таких как $'\t', для представления символа табуляции.


Справочная информация :

  • Спецификация POSIX sed: http://man.cx/sed
    • BSD sed (также используется в macOS) остается близким к этой спецификации, в то время как GNU sed предлагает множество расширений.
  • Сводку различий между GNU sed и BSD sed можно найти по адресу https://stackoverflow.com/a/24276470/45375
8 голосов
/ 24 мая 2011

Версия Solaris sed Я мог бы убедить работать таким образом (в bash):

echo test1234foo123bar1234 | sed 's/\(1234\)/\
\1/g'

(вы должны поставить разрыв строки сразу после обратной косой черты).

В csh Мне пришлось добавить еще одну обратную косую черту:

echo test1234foo123bar1234 | sed 's/\(1234\)/\\
\1/g'

Gnu-версия sed просто работала с использованием \n:

echo test1234foo123bar1234 | sed 's/\(1234\)/\n\1/g'
8 голосов
/ 24 мая 2011

Perl предоставляет более богатый "расширенный" синтаксис регулярных выражений, который полезен здесь:

perl -p -e 's/(?=1234)/\n/g'

означает «заменить новую строку на совпадение с нулевой шириной по шаблону 1234». Это избавляет от необходимости захватывать и повторять часть выражения с обратными ссылками.

2 голосов
/ 03 октября 2014

Получить GNU sed .

$ brew install gnu-sed

Тогда ваша команда будет работать как положено:

$ gsed "s/\(1234\)/\n\1/g" input.txt
test
1234foo123bar
1234

Примечание: вы можете получить GNU sed благодаря портам Mac.

2 голосов
/ 24 мая 2011

К сожалению, для меня sed, кажется, игнорирует \n s в строке замены.

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\n\1/g"
testn1234foo123barn1234

Если это также происходит с вами, альтернативой является использование:

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\\`echo -e '\n\r'`\1/g"

Это должно работать где угодно и будет производить:

test
1234foo123bar
1234

Для вашего примера с файлом input.txt в качестве ввода и output.txt в качестве вывода, используйте:

$ sed "s/\(1234\)/\\`echo -e '\n\r'`\1/g" input.txt > output.txt
1 голос
/ 08 марта 2013

Новая строка в середине команды может показаться немного неуклюжей:

$ echo abc | sed 's/b/\
/'
a
c

Вот два решения этой проблемы, которые, я думаю, должны быть достаточно портативными. (должно работать для любых POSIX-совместимых sh, printf и sed):

Решение 1:

Не забудьте экранировать любые символы \ и % для printf здесь:

$ echo abc | sed "$(printf 's/b/\\\n/')"
a
c

Чтобы избежать необходимости экранировать символы \ и % для printf:

$ echo abc | sed "$(printf '%s\n%s' 's/b/\' '/')"
a
c

Решение 2:

Создайте переменную, содержащую новую строку, например:

newline="$(printf '\nx')"; newline="${newline%x}"

Или вот так:

newline='
'

Тогда используйте это так:

$ echo abc | sed "s/b/\\${newline}/"
a
c
1 голос
/ 24 мая 2011

Вы также можете использовать функцию $'string' Bash:

man bash | less -p "\\$'"

printf  '%s' 'test1234foo123bar1234'  | sed $'s/\\(1234\\)/\\\n\\1/g'
1 голос
/ 24 мая 2011

Попробуйте это:

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\n\1/g"
test
1234foo123bar
1234

С Сед Гну Док

g
    Apply the replacement to all matches to the regexp, not just the first. 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...