Поняття "утримувати простір" та "простір візерунка" в 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.

Ці шість команд мене справді бентежать.


4
Спробуйте самі:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba

4
Не плутайте, просто не використовуйте їх. Для будь-чого іншого, крім простих підстановок в одному рядку, ви повинні використовувати awk, а не sed. Пробіли для зберігання, пробіли для шаблонів та 95% конструкцій мови sed були винайдені ще до awk, коли не було кращої альтернативи. Вони застаріли, як тільки awk був винайдений в середині 1970-х років, і сьогодні їх підтримують лише люди, яким подобається вирішувати проблеми за допомогою тайного синтаксису seds, а не робити це просто та грубо в awk. Якщо ви використовуєте більше, ніж s, g та p (з -n) в sed, то ви майже напевно використовуєте неправильний інструмент.
Ed Morton

26
Morton awk працює зі структурованими даними (кожен рядок має однакову структуру). Sed призначений для роботи з необробленими випадковими даними. Отже, ви не можете просто використовувати awk замість sed.
Пітікос,

5
Настійно рекомендую прочитати info sed. Це набагато детальніше, ніж проста сторінка.
Фернандо Бассо

4
Я згоден з Пітікосом. Я спустився по провулку, як це зробив Мортон, і задав собі те саме питання, що й Мортон. Однак я ще не міг відпустити Sed так легко.
eigenfield

Відповіді:


111

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

Простір буфера / утримання схожий на довготривале сховище, таке що ви можете щось схопити, зберегти і повторно використовувати пізніше, коли sed обробляє інший рядок. Ви безпосередньо не обробляєте простір утримання, натомість вам потрібно скопіювати його або додати до простору зразків, якщо ви хочете щось з ним зробити. Наприклад, команда print 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 як "вирізати та додати" ?? Це не схоже на операцію "копіювання та додавання".
Посміхніться

Що додається до шаблону та утримує простір, коли використовуються вкладені команди (фігурні дужки)? '195,210{/add/p}'... чи можна витягти останній рядок групи рядків, задіяних у шаблоні?
Сандбург

17

@ Ed Morton: Я тут з вами не згоден. Я виявив 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:grep 'Host\|Info'
Пітікос

Якщо після даного хосту є два інформаційні рядки, тоді @JensJenson хоче, щоб обидва інформаційні рядки передували інформаційним рядком. Думаю, відповідне я відредагую. Пітікос, тоді grep буде недостатньо.
Аарон Макдейд

3
@JensJenson, awkеквівалент вашого коду sed теж теж короткий:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid

11

Хоча відповідь і приклад @ січня приємні, пояснення мені було недостатньо. Мені довелося багато шукати і вчитися, поки не вдалося зрозуміти, як саме 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.