Концепция `` удерживаемого пространства '' и `` пространства шаблонов '' в sed

86

Меня смущают две концепции в sed: пространство для хранения и пространство шаблонов. Может кто-нибудь помочь им объяснить?

Вот отрывок из руководства:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

Эти шесть команд меня действительно сбивают с толку.

ChenQi
источник
4
Попробуйте сами:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba
4
Не путайте, просто не используйте их. Для чего-либо, кроме простых замен в одной строке, вы должны использовать awk, а не sed. Пробелы хранения, пространства шаблонов и 95% конструкций языка sed были изобретены до awk, когда не было лучшей альтернативы. Они устарели, как только в середине 1970-х был изобретен awk, и продолжают существовать только сегодня людьми, которым нравится решать проблемы с использованием тайного синтаксиса seds, а не делать это просто и аккуратно в awk. Если вы используете в sed больше s, g и p (с -n), то вы почти наверняка используете неправильный инструмент.
Эд Мортон
26
Morton awk работает со структурированными данными (каждая строка имеет одинаковую структуру). Sed предназначен для работы с необработанными случайными данными. Таким образом, вы не можете просто использовать awk вместо sed.
Pithikos
5
Настоятельно рекомендую прочитать info sed. Он гораздо более подробный, чем простая справочная страница.
Фернандо Бассо
4
Я согласен с Питикосом. Я пошел по переулку, как сделал Мортон, и задал себе тот же вопрос, что и Мортон. Однако я еще не мог так легко уволить sed.
eigenfield 06

Ответы:

111

Когда СЭД читает файл построчно, линия , которая была в настоящее время чтения вставляется в шаблон буфера (модель пространства). Буфер шаблонов похож на временный буфер, блокнот, в котором хранится текущая информация. Когда вы приказываете sed печатать, он печатает буфер шаблонов.

Буфер удержания / пространство удержания похоже на долгосрочное хранилище, так что вы можете что-то поймать, сохранить и повторно использовать позже, когда sed обрабатывает другую строку. Вы не обрабатываете пространство удержания напрямую, вместо этого вам нужно скопировать его или добавить в пространство шаблона, если вы хотите что-то с ним сделать. Например, команда печати pпечатает только пространство шаблона. Точно так же sработает с пространством шаблонов.

Вот пример:

sed -n '1!G;h;$p'

(опция -n подавляет автоматическую печать строк)

Есть три команды здесь: 1!G, hи $p. 1!Gимеет адрес 1(первая строка), но это !означает, что команда будет выполняться везде, кроме первой строки. $pс другой стороны, будет выполняться только в последней строке. Так вот что происходит:

  1. первая строка считывается и автоматически вставляется в пространство шаблонов
  2. в первой строке первая команда не выполняется; hкопирует первую строку в удерживаемое пространство.
  3. теперь вторая строка заменяет все, что было в пространстве шаблонов
  4. во второй строке мы сначала выполняем G, добавляя содержимое буфера удержания к буферу шаблона, разделяя его новой строкой. Пространство шаблонов теперь содержит вторую строку, новую строку и первую строку.
  5. Затем hкоманда вставляет объединенное содержимое буфера шаблона в область удержания, которая теперь содержит перевернутые строки две и одну.
  6. Переходим к строке номер три - переходим к пункту (3) выше.

Наконец, после того, как последняя строка была прочитана и пространство удержания (содержащее все предыдущие строки в обратном порядке) было добавлено к пространству образца, пространство образца печатается с p. Как вы уже догадались, приведенное выше делает именно то, что tacделает команда - печатает файл в обратном порядке.

Январь
источник
3
Опция G и h работает как «вырезать и добавить» ?? Это не похоже на операцию «скопировать и добавить».
Smile
Что добавляется с шаблоном и удерживает пробел при использовании вложенных команд (фигурные скобки)? '195,210{/add/p}'… Можно ли извлечь последнюю строку из группы строк, входящих в паттерн?
Sandburg
17

@ Эд Мортон: Я с вами не согласен. Я нашел sedочень полезным и простым (как только вы разберетесь с концепцией шаблона и буферов хранения) придумать элегантный способ выполнения многострочного поиска.

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

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

Для меня сценарий awk, который просто получает строки с именем хоста и соответствующей infoстрокой, занимает немного больше, чем то, что я могу сделать с помощью sed:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

вывод выглядит так:

Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

(Обратите внимание, что Host: foo1на выходе появляется дважды.)

Пояснение:

  1. -n отключает вывод, если явно не напечатан
  2. первое совпадение, находит и помещает Host:строку в буфер удержания (h)
  3. второе совпадение, находит следующую строку Info :, но сначала меняет (x) текущую строку в буфере шаблонов на буфер хранения и печатает (p) Host:строку, затем повторно меняет (x) и печатает (p) строку Info :.

Да, это упрощенный пример, но я подозреваю, что это обычная проблема, с которой быстро справился простой однострочник sed. Для гораздо более сложных задач, таких как те, в которых вы не можете полагаться на заданную предсказуемую последовательность, awk может лучше подойти.

Йенс Йенсен
источник
2
В этом случае вы могли бы просто использовать grep 'Host\|Info'
команду
Если после заданного Host есть две строки Info, то @JensJenson хочет, чтобы обеим строкам Info предшествовала строка Info. Думаю, я соответствующим образом отредактирую ответ. Pithikos, тогда grep не хватит.
Aaron McDaid
3
@JensJenson, awkэквивалент вашего кода sed тоже довольно короткий:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Аарон МакДэйд,
11

Несмотря на то, что ответ @ January и пример хороши, объяснения мне было недостаточно. Мне пришлось много искать и узнавать, пока я не смог понять, как именно sed -n '1!G;h;$p'работает. Поэтому я хотел бы подробнее рассказать о команде для кого-то вроде меня.

Прежде всего, давайте посмотрим, что делает команда.

$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a

Он меняет ввод, как это tacделает команда.

sedчитает построчно, поэтому давайте посмотрим, что происходит в пространстве шаблона и в пространстве удержания в каждой строке. Поскольку hкоманда копирует содержимое пространства шаблона в пространство удержания, оба пространства имеют одинаковый текст.

Read line    Pattern Space / Hold Space    Command executed
-----------------------------------------------------------
a            a$                            h
b            b\na$                         1!G;h
c            c\nb\na$                      1!G;h
d            d\nc\nb\na$                   1!G;h;$p

В последней строке $pпечатается d\nc\nb\na$формат,

d
c
b
a

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

$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a

Мне было очень полезно посмотреть этот видеоурок Понимание того , как работает sed , поскольку парень шаг за шагом показывает, как каждое пространство будет использоваться. Об интервале удержания упоминается в 4-м уроке, но я рекомендую посмотреть все видео, если вы не знакомы с ними sed.

Также очень хорошими ссылками являются документ GNU sed и учебник Брюса Барнетта по Sed .

Санхюн Ли
источник
2
Я думаю, также будет полезно упомянуть, что удерживаемое пространство для всех практических целей пусто, если мы не добавим к нему что-то.
Naveed