Концепция "Hold space" и "Pattern space" в sed
Я смущен двумя понятиями в 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.
Эти шесть команд действительно путают меня.
Ответы
Ответ 1
Когда sed читает файл строки за строкой, строка, которая была прочитана в настоящее время, вставляется в буфер шаблона (пространство шаблонов). Буфер шаблонов похож на временный буфер, блокнот, в котором хранится текущая информация. Когда вы сообщаете sed для печати, он печатает буфер шаблонов.
Удержание буфера/удержания - это как долговременное хранилище, так что вы можете что-то поймать, сохранить его и повторно использовать позже, когда sed обрабатывает другую строку. Вы напрямую не обрабатываете пространство удержания, вместо этого вам нужно скопировать его или добавить в пространство шаблонов, если вы хотите что-то с ним сделать. Например, команда print p
печатает только пространство с рисунком. Аналогично, s
работает на пространстве шаблонов.
Вот пример:
sed -n '1!G;h;$p'
(опция -n подавляет автоматическую печать строк)
Здесь есть три команды: 1!G
, h
и $p
. 1!G
имеет адрес, 1
(первая строка), но !
означает, что команда будет выполняться всюду, но в первой строке. $p
, с другой стороны, будет выполняться только в последней строке. Итак, что происходит:
- первая строка считывается и автоматически добавляется в пространство шаблонов
- в первой строке первая команда не выполняется;
h
копирует первую строку в пространство удержание.
- теперь вторая строка заменяет все, что было в пространстве шаблонов
- во второй строке, сначала мы выполним
G
, добавив содержимое буфера удержания в буфер шаблона, разделив его на новую строку. Теперь пространство шаблонов содержит вторую строку, новую строку и первую строку.
- Затем команда
h
вставляет конкатенированное содержимое буфера шаблона в пространство удержания, которое теперь содержит обратные строки два и один.
- Переходим к строке номер три - переходим к точке (3) выше.
Наконец, после того, как последняя строка была прочитана, и пространство удержания (содержащее все предыдущие строки в обратном порядке) было добавлено к пространству рисунка, пространство с рисунком печатается с помощью p
. Как вы уже догадались, вышесказанное делает именно то, что делает команда tac
- печатает файл в обратном порядке.
Ответ 2
@Эд Мортон: я не согласен с вами здесь. Я обнаружил, что 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
появляется дважды в выводе.)
Объяснение:
-
-n
отключает вывод, если явно не напечатано - первое совпадение, находит и помещает строку
Host:
в буфер удержания (h) - во втором совпадении находит следующую строку Info:, но сначала обменивается (x) текущей строкой в буфере шаблонов с буфером удержания, и печатает (p) строку
Host:
затем повторно обменивается (x) и печатает (p) Info: линия.
Да, это упрощенный пример, но я подозреваю, что это общая проблема, с которой быстро справился простой однострочник sed. Для гораздо более сложных задач, таких как задачи, в которых вы не можете полагаться на заданную, предсказуемую последовательность, может лучше подойти awk.
Ответ 3
Хотя ответ @января и пример хороши, объяснения мне не хватило. Мне пришлось много искать и учиться, пока я не понял, как именно работает 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
читает строку за строкой, так что давайте посмотрим, что происходит в области patten и в области hold в каждой строке. Поскольку команда 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, поскольку парень показывает, как каждое пространство будет использоваться шаг за шагом. Интервал удержания указан в четвертом уроке, но я рекомендую посмотреть все видео, если вы не знакомы с sed
.
Также документ GNU sed и учебник Bruce Barnett Sed являются очень хорошими ссылками.