Как я могу использовать sed для замены многострочной строки?

Я заметил, что если я добавлю \ n в шаблон для замены с помощью sed , он не соответствует. Пример:

  $ cat> alpha.txt
Это
тест
Пожалуйста, не надо
быть встревожен

$ sed -i'.original '' s /a test \ nПожалуйста, не выполняйте /не проверяйте \ nBe /'alpha.txt

$ diff alpha.txt {,. original}

$ # Различий нет
 

Как я могу заставить это работать?

186 голосов | спросил Belmin Fernandez 7 WedEurope/Moscow2011-12-07T22:03:28+04:00Europe/Moscow12bEurope/MoscowWed, 07 Dec 2011 22:03:28 +0400 2011, 22:03:28

12 ответов


184

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

Вы можете читать несколько строк в пространстве шаблонов и управлять ими на удивление хорошо, но с более чем обычным усилием. Sed имеет набор команд, которые позволяют использовать этот тип ... Вот ссылка на < a href = "http://docstore.mik.ua/orelly/unix/sedawk/appa_03.htm"> Краткое описание команд для sed . Это лучший, который я нашел, и заставил меня прокатиться.

Однако запустите идею «однострочного», как только вы начнете использовать микрокоманды sed. Полезно выложить его как структурированную программу, пока вы не почувствуете ее ... Это удивительно просто и не менее необычно. Вы могли бы подумать об этом как о «языке ассемблера» редактирования текста.

Резюме: используйте sed для простых вещей и, возможно, немного больше, но в целом, когда он выходит за рамки работы с одной строкой, большинство людей предпочитают что-то еще ...
Я позволю кому-то еще предложить что-то еще. Я действительно не уверен, каким будет лучший выбор (я бы использовал sed, но это потому, что я не знаю perl достаточно хорошо.)


  sed '/^ тест $ /{
       $! {N # добавить следующую строку, если не на последней строке
         s /^ тест \ nПожалуйста, не $ /не тест \ nBe /
                    # теперь проверяем на успешную замену, иначе
                    # + непарные «тестовые» строки будут неправильно обработаны
         t sub-yes # branch_on_substitute (метка goto: sub-yes)
         : суб-не # ярлык (не обязательно, здесь для самостоятельного документа)
                    # если нет заместителя, напечатайте только первую строку
         P # pattern_first_line_print
         D # pattern_ltrunc (строка + nl) _top /цикл
         : sub-yes # ярлык (цель goto ветки 't')
                    # проваливается до окончательного auto-pattern_print (2 строки)
       }
     } 'alpha.txt
 

Здесь это тот же самый сценарий, сжатый в то, что, очевидно, труднее читать и работать, но некоторые сомневались бы назвать однострочным

  sed '/^ тест $ /{$! {N; s /^ тест \ nПожалуйста, не $ /не тест \ nBe /; ty; P; D;: y}}' alpha .текст
 

Вот моя команда «cheat-sheet»

 : # label
= # line_number
a # append_text_to_stdout_after_flush
b # branch_unconditional
c # range_change
d # pattern_delete_top /цикл
D # pattern_ltrunc (строка + nl) _top /цикл
g # pattern = hold
G # pattern + = nl + hold
h # hold = pattern
H # hold + = nl + pattern
i # insert_text_to_stdout_now
l # pattern_list
n # pattern_flush = nextline_continue
N # pattern + = nl + nextline
p # pattern_print
P # pattern_first_line_print
q # flush_quit
r # append_file_to_stdout_after_flush
s # заменить
t # branch_on_substitute
w # append_pattern_to_file_now
x # swap_pattern_and_hold
y # transform_chars
 
ответил Peter.O 8 ThuEurope/Moscow2011-12-08T00:45:35+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 00:45:35 +0400 2011, 00:45:35
140

Используйте perl вместо sed :

  $ perl -0777 -i.original -pe 's /a test \ nПожалуйста, не выполняйте /не тестируйте \ nBe /igs' alpha.txt
$ diff alpha.txt {,. original}
2,3c2,3
& Lt; не испытание
& Lt; Быть
---
& GT; тест
& GT; Пожалуйста, не надо
 

-pi -e - это стандартная последовательность «замена на месте» командной строки, а -0777 вызывает perl для slurp-файлов в целом. См. perldoc perlrun , чтобы узнать больше об этом.

ответил codehead 8 ThuEurope/Moscow2011-12-08T00:20:15+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 00:20:15 +0400 2011, 00:20:15
73

Я думаю, лучше заменить символ \ n другим символом, а затем работать как обычно:

например. неиспользуемый исходный код:

  cat alpha.txt | sed -e 's /a test \ nПожалуйста, не выполняйте /не тест \ nBe /'
 

можно изменить на:

  cat alpha.txt | tr '\ n' '\ r' | sed -e 's /a test \ rПожалуйста, не выполняйте /не тест \ rBe /' | tr '\ r' '\ n'
 

Если кто-то не знает, \ n - конец строки UNIX, \ r \ n - windows, \ r - классический Mac ОПЕРАЦИОННЫЕ СИСТЕМЫ. Обычный текст UNIX не использует символ \ r , поэтому безопасно использовать его для этого случая.

Вы также можете использовать некоторый экзотический символ, чтобы временно заменить \ n. В качестве примера - \ f (символ подачи формы). Вы можете найти больше символов здесь .

  cat alpha.txt | tr '\ n' '\ f' | sed -e 's /a test \ fПожалуйста, не выполняйте /не тест \ fBe /' | tr '\ f' '\ n'
 
ответил xara 27 PM00000020000002831 2014, 14:03:28
35

sed имеет три команды для управления многолинейными операциями: N , D и P (сравните их с normal n , d и p ).

В этом случае вы можете сопоставить первую строку своего шаблона, используйте N , чтобы добавить вторую строку в пространство шаблонов , а затем используйте s , чтобы сделать вашу замену.

Что-то вроде:

  /test $ /{
  N
  s /a test \ nПожалуйста, не выполняйте /не тестируйте \ nBe /
}
 
ответил andcoz 8 ThuEurope/Moscow2011-12-08T00:17:18+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 00:17:18 +0400 2011, 00:17:18
31

Все рассмотренные вещи, gobbling весь файл может быть самым быстрым способом.

Основной синтаксис выглядит следующим образом:

  sed -e '1h; 2, $ H; $! d; g' -e 's /__ YOUR_REGEX_GOES_HERE __...'
 

Помните, что копирование всего файла может не быть вариантом, если файл чрезвычайно большой. В таких случаях другие ответы, представленные здесь, предлагают индивидуальные решения, гарантирующие работу с небольшим объемом памяти.

Для всех других ситуаций взлома и слэша, просто добавляя -e '1h; 2, $ H; $! d; g' , за которым следует исходный аргумент sed regex в значительной степени выполняет свою работу.

например.

  $ echo -e "Собака \ nFox \ nCat \ nSnake \ n" | sed -e '1h; 2, $ H; $! d; g' -re 's /([^ \ n] *) \ n ([^ \ n] *) \ n /Quick \ 2 \ nLazy \ 1 \ п /г»
Быстрая лиса
Лентяй
Быстрая змея
Ленивый кот
 

Что делает -e '1h; 2, $ H; $! d; g' do?

Компоненты 1 , 2, $ , $! - это спецификаторы строк, которые ограничивают, в каких строках выполняется следующая следующая команда.

  • 1 : только первая строка
  • 2, $ : все строки, начинающиеся со второго
  • $! : каждая строка, отличная от последней

Так расширен, это то, что происходит в каждой строке ввода строки N.

  1: h, d
  2: H, d
  3: H, d
  ,
  ,
N-2: H, d
N-1: H, d
  N: H, g
 

Команде g не задан спецификатор строки, но предыдущая команда d имеет специальное предложение " Начать следующий цикл. ", и это предотвращает запуск g во всех строках, кроме последнего.

Что касается значения каждой команды:

  • Первый h , за которым следует H s на каждой строке, копирует указанные строки ввода в sed 's пробел . (Думайте произвольный текстовый буфер.)
  • Затем d отбрасывает каждую строку, чтобы эти строки не записывались на выход. Пространство hold сохраняется.
  • Наконец, в последней строке g восстанавливается накопление каждой строки из пространства hold , чтобы sed мог запускаться его регулярное выражение на всем входе (а не в строке по времени), и, следовательно, может соответствовать на \ n s.
ответил antak 9 +03002015-10-09T15:58:39+03:00312015bEurope/MoscowFri, 09 Oct 2015 15:58:39 +0300 2015, 15:58:39
13

Вы можете, но это сложно . Я рекомендую переключиться на другой инструмент. Если существует регулярное выражение, которое никогда не соответствует какой-либо части текста, которую вы хотите заменить, вы можете использовать его в качестве разделителя записей awk в GNU awk.

  awk -v RS = 'a' '{gsub (/hello /, "world"); Распечатать}'
 

Если в строке поиска никогда нет двух последовательных строк новой строки, вы можете использовать режим абзаца awk (одна или несколько пустых строк для отдельных записей).

  awk -v RS = '' '{gsub (/hello /, "world"); Распечатать}'
 

Простое решение - использовать Perl и полностью загрузить файл в память.

  perl -0777 -pe 's /hello /world /g'
 
ответил Gilles 8 ThuEurope/Moscow2011-12-08T03:11:42+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 03:11:42 +0400 2011, 03:11:42
5
  sed -i'.original '' /a test /, /Не делайте /c не тест \ nBe 'alpha.txt
 

Здесь /a test /, /Пожалуйста, не / рассматривается как блок (многострочный) текст, c - это изменить команду , а затем новый текст not the test \ nBe

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

ответил gibies 16 PM00000010000004431 2016, 13:38:44
4

Помимо Perl, общий и удобный подход для многострочного редактирования для потоков (и файлов тоже):

Сначала создайте новый разделитель строк UNIQUE, как вам нравится, например

  $ S = __ ABC__ # simple
$ S = __ $ RANDOM $ RANDOM $ RANDOM__ # лучше
$ S = $ (openssl rand -hex 16) # ultimate
 

Затем в вашей команде sed (или любом другом инструменте) вы заменяете \ n на $ {S}, например

  $ cat file.txt | awk 1 ORS = $ S | sed -e "s /a test $ {S} Пожалуйста, не проверяйте \ nBe /" | awk 1 RS = $ S> file_new.txt
 

(awk заменяет разделитель строк ASCII на ваш и наоборот).

ответил guest 8 J0000006Europe/Moscow 2017, 09:07:46
3
  sed -e '$! N; s /^ \ (тест \ n \) Пожалуйста, не стоит $ /not \ 1Be /; P; D' <in> out
 

Просто немного увеличьте свое окно на входе.

Это довольно легко. Помимо стандартной замены; вам нужен только $! N , P и D .

ответил mikeserv 24 Jpm1000000pmSun, 24 Jan 2016 22:26:27 +030016 2016, 22:26:27
2

Это небольшая модификация умного ответа xara, чтобы он работал на OS X (я использую 10.10):

  cat alpha.txt | tr '\ n' '\ r' | sed -e 's /a test $ (printf' \ r ') Пожалуйста, не выполняйте /не тест $ (printf' \ r ') Be /' | tr '\ r' '\ n'
 

Вместо явного использования \ r , вы должны использовать $ (printf '\ r') .

ответил abeboparebop 11 FebruaryEurope/MoscowbThu, 11 Feb 2016 18:36:00 +0300000000pmThu, 11 Feb 2016 18:36:00 +030016 2016, 18:36:00
2

Я думаю, что это решение sed для соответствия двух строк.

  sed -n '$! N; s @ test \ nПожалуйста, не @ не тест \ nBe @; P; D' alpha.txt
 

Если вам нужны 3 строки, то ...

  sed -n '1 {$! N}; $! N; s @ aaa \ nbbb \ nccc @ xxx \ nyyy \ nzzz @; P; D'
 

Если вы хотите, чтобы 4 строки соответствовали ...

  sed -n '1 {$! N; $! N}; $! N; s @ ... @ ... @; P; D'
 

Если сменная часть в команде «s» сокращает линии затем немного сложнее, чем это.

  # aaa \ nbbb \ nccc сжимается до одной строки "xxx"

sed -n '1 {$! N}; $! N; /aaa \ nbbb \ nccc /{s @@ xxx @; $! N; $! N}; P; D'
 

Если часть повторения затягивает линии, то немного сложнее, чем это

  # aaa \ nbbb \ nccc вырастите до пяти строк vvv \ nwww \ nxxx \ nyyy \ nzzz

sed -n '1 {$! N}; $! N; /aaa \ nbbb \ nccc /{s @@ vvv \ nwww \ nxxx \ nyyy \ nzzz @; P; s /.* \ n //M; P ; s /.* \ п //М}, P, D»
 
ответил mug896 14 Jpm1000000pmSat, 14 Jan 2017 19:55:22 +030017 2017, 19:55:22
1

Я хотел добавить несколько строк HTML в файл с помощью sed (и оказался здесь). Обычно я просто использовал perl, но я был на коробке с sed, bash и не намного больше. Я обнаружил, что если бы я изменил строку на одну строку и позволил bash /sed интерполировать \ t \ n, все получилось:

  HTML_FILE = 'a.html' # содержит якорь в форме <имя = "nchor" /& gt;
BASH_STRING_A = 'яблоки'
BASH_STRING_B = 'бананы'
INSERT = "\ t <li> $ BASH_STRING_A <\ /li> \ n \ t <li> $ BASH_STRING_B <\ /li> \ n <a name = \" nchor \ "\ /& gt;"
sed -i "s /<имя = \" nchor "\ /& gt; /$ INSERT /" $ HTML_FILE
 

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

ответил Alexx Roche 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 20 Sep 2016 23:33:12 +0300 2016, 23:33:12

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132