SED і AWK

  1. Чи є Sed мовою?
  2. Заміщення за допомогою Sed
  3. вибір рядків
  4. Шаблони і простір утримання
  5. Приклад зсуву функцій
  6. Sed на практиці

На цей раз я маю намір розглянути два малих мови, популярних в Лінуксі - Sed і Awk

На цей раз я маю намір розглянути два малих мови, популярних в Лінуксі - Sed і Awk. Обидва широко застосовуються в системному адмініструванні і дуже в цьому допомагають, якщо ви звичайно ж, розумієте код, навіть не написавши жодного рядка. Подивимося ж на Sed.

Чи є Sed мовою?

Можна було б погодитися з тим, що набір команд Sed не є мовою програмування. Насправді, чудовий скрипт Крістофера Блесс підтверджує, що Sed повний по Тьюрингу, а це значить теоретичне відповідність іншим мовам програмування. Джулія Джоманте навіть написала гру Тетріс на Sed. Але, звичайно ж, ніхто найближчим часом не напише ядро ​​Лінукса на ньому.

Для початку - Sed є потоковим редактором і працює як класичний фільтр, приймаючи на вхід обробляється файл і передаючи оброблені дані наступній команді в потоці. Sed читає з вхідного файлу або зі стандартного вводу (stdin) по одному рядку, виконує запропоноване дію і передає підсумок на стандартний висновок (stdout). Потім читає наступний рядок і т.д. На відміну від звичайного текстового редактора, що приймає весь файл в буфер, Sed поміщає в нього тільки один рядок, результативно обробляючи величезні файли.

Заміщення за допомогою Sed

Почнемо з повсякденного для адміністратора прикладу виконання заміни. Припустимо, що ми перемістили домашні каталоги з / home в / users і нам потрібно замінити в / etc / passwd все такі рядки

chris: x: 501: 501 :: / home / chris: / bin / bash

на

chris: x: 501: 501 :: / users / chris: / bin / bash

Це можна зробити такий коммандой:

sed s / home / users / / etc / passwd

Тут Sed читає файл passwd по одному рядку, робить заміну і виводить результат в stdout. Він не змінює оригінального файлу, а якщо ж його потрібно змінити, то виконуємо:

sed s / home / users / / etc / passwd> / etc / passwd

Але тут ховається каверза - шелл, виявивши перенаправлення виведення, обріже вихідний файл до нульової довжини, перш ніж Sed побачить його, і прощай файл паролів! Це звичайна справа для фільтрів - не можна перенаправити вхідний файл на себе ж. Замість цього потрібно робити так:

sed s / home / users / / etc / passwd> / tmp / passwd mv / tmp / passwd / etc / passwd '

Насправді, в GNU версії Sed є ключ -i, що дозволяє це, і команда:

sed -is / home / users / / etc / passwd

зробить все правильно, але, стережіться залишить цей файл passwd, якщо не впевнені в тому, що ваша версія Sed робить так, як задумано.

Наступний приклад навіть простіше, команда df виводить таблицю використання дисків для всіх файлових систем в системі, але також і заголовок, який заважає в потокової обробці. Його можна прибрати командою

df | sed 1d

Тут Sed читає вхідний потік, який є вихідним для df. Команда d означає - видалити рядок, а 1 - тільки рядок 1. Следоватльно, перший рядок вирізається, а все інше віддається без змін. Це відповідає tail -n +2.

Помотрім знову на команду s (substitute / замінити). Припустимо, що нам потрібно отримати імена користувачів з файлу / etc / passwd. Видно, що ім'я знаходиться в першій колонці. Неважко виявити, що частина'старого шаблона' заміни - це регулярний вираз

sed s /: / * // / etc / passwd

Цей дуже хитрий приклад, тут'старий шаблон', це regex \ ':. * \' Означає, від першої колонки, до кінця рядка. Тут ми покладаємося на'жадность' регекспів - відповідність починається якомога раніше і триває наскільки можливо. 'Новий шаблон' порожній, тому все, що регекспів знайде, видаляється. Просто диво!

Ще приклад на заміну: потрібно замінити рядки "$ 25" на "25 USD". Це трохи складніше, тому що "GBP" має стояти після числа.

sed -r 's / $ ([0-9] *) / \ 1 USD / g' prices

що змінить рядок

fees range from $ 25 to $ 40 typically

на

fees range from 25 USD to 40 USD typically

Синтакс Sed ускладнюється дуже швидко. Розберемо цей приклад зворотного заміни. Ключ -r включає розширений режим, '$ ([0-9] *)' - це'старий шаблон', де '[0-9] *''отмеченная' чась регекспів відповідна будь-якій послідовності чисел. '\ 1 USD' - це'новий шаблон', де '\ 1','обратная замена', вставляє відповідну'отмеченную' частина регулярного виразу. команда g робить заміну всіх відповідників рядки.

Складно? Але в адміністративних скриптах можна знайти і більш чудові приклади команди Sed. Наприклад, у файлі / etc / init / rc-sysinit / conf в Убунту можна побачити:

sed -nre 's / ^ [^ #] [^:] * :( 0-6sS]): initdefault:. * / DEFAULT_RUNLEVEL = "\ 1 |; / p' / etc / inittab

ця команда просто витягує default run level з файлу inittab.

Зазвичай для поділу частин в командах заміни використовуються прямі слеші, і, якщо самі шаблони містять їх, це призводить до дуже наворочений рядках, наприклад:

sed 's / \ / home \ / chris \ / bin / \ / opt \ / bin /' foo.txt

В цьому випадку можна використовувати інший роздільник, наприклад ':'. Так і виглядає краще:

sed 's: / home / chris / bin "/ opt / bin:' foo.txt

вибір рядків

Для редагування можна вибирати один рядок або інтервал рядків. Раніше ми бачили команду 1d для вибору 1-го рядка. Для видалення інтервалу рядків, наприклад, з 1-го по 10-ю, даємо команду 1,10d або 5, $ d для видалення з 5-го рядка до кінця файлу. Також можна вибирати рядки за допомогою регулярний виразів.

така команда

sed '/ ^ # / d' / etc / fstab

видалить рядки, що починаються з символу #, зазвичай так відзначаються коментарі. Це як би зворотний grep (друкує незбіжні рядки). Для отримання звичайної поведінки grep, потрібно, по-перше, додати ключ -n вимикає автоматичну друк рядків, по-друге, недвозначно сказати йому друкувати потрібні рядки / p:

sed -n '/ ^ # / p' / etc / fstab

Зауважте одинарні лапки в команді, для запобігання неоднозначностей в командному рядку Лінукс.

Більш цікавий приклад: є шелл-скрипт з безліччю визначень функцій, розкиданих по ньому, і потрібно витягти їх в окремий файл.

#! / Bin / bash echo привіт function foo () {echo це перша} # виклик першої функції foo function bar () {echo це друга} # виклик другої функції bar

Для початку, зробимо скрипт з вирізаними визначення функцій:

sed '/ ^ function /, / ^} / d' demo.sh> demo2.sh

Тут ми визначили інтервал номерів рядків на основі відповідності регулярному виразу. Текст між рядком з початком функції і до} видаляється, і якщо є кілька таких блоків, всі вони втечуть. Далі, залишається тільки витягти їх в потрібний файл:

sed -n '/ ^ function /, / ^} / d' demo.sh> funcs.sh

Греп так не вийде!

Шаблони і простір утримання

Навіть кількома простими командами, разом з хитрим використанням регулярних виразів, ми змогли зробити дуже багато і це не межа можливостей Sed. Але у всіх наших прикладах вихідні рядки йдуть в тому ж порядку, що і вхідні. Змінити порядок рядків у файлі не вийде. Для цього потрібно зрозуміти що таке'пространство шаблонов' і'пространство удержанія'. Простір шаблонів - це текстовий буфер, що використовується в нормальному, рядок за рядком, редагуванні. Команда заміни, наприклад, працює в ньому і команда p виводить його вміст на друк.

Простір утримання - це буфер, де затримується текст, для, наприклад, зміни порядку проходження рядків. Три основні команди h, H і x переносять текст в і з нього (є й інші, див. Man sed).

Для використання простору утримання необхідно виконувати дві або декілька команд Sed за один виклик, і ось як це можна зробити. Перший спосіб, задати ключ -e в командному рядку:

sed -e 's / linux / windows /' -e 's / good / bad /' somefile.txt

тут виробляються обидві заміни в кожному рядку. Другий спосіб, розділити команди крапкою з комою:

sed -e 's / linux / windows /; s / good / bad /' somefile.txt

Це все добре, якщо команд мало, але есді їх стає все більше, то краще записати їх у файл і посилатися на нього в командному рядку. Наприклад, у файлі script.sed є такі рядки:

s / linux / windows / s / good / bad /

Тепер можна викликати Sed так:

sed -f script.sed somefile.txt

Переваги такого способу в тому, що не потрібно брати команди в лапки, бо Шеллу вже не потрібно їх інтерпретувати і готовий скрипт можна використовувати повторно.

З огляду на все це, повернемося до нашого скрипту і перемістимо визначення функція в початок файлу, із залишком скрипта внизу:

# Sed-скрипт для переміщення функцій в шелл-скрипті / ^ function /, / ^} /! H / ^ function /, / ^} /! P $ {x; p}

Приклад зсуву функцій

Тут необхідні деякі пояснення. Перший рядок скрипта містить ту ж пару регулярних виразів для пошуку тіла функції, що й колись, з додаванням знака! - реверсування значення. Команда H додає простір шаблонів до простору утримання, так що вибудовує в буфері утримання все рядки, що знаходяться поза визначень функцій. Другий рядок скрипта друкує ті рядки, що містять визначення функцій, так що вони виходять першими, як і було потрібно. І, нарешті, останній рядок, використовуючи скорочення $ від номера рядків, що означає'последнюю рядок входа', змінює місцями простір утримання і простір шаблонів і друкує їх.

Перевіримо, що пролучілось:

$ Sed -n -f splitout.sed demoscript.sh function foo () {echo це перша} function bar () {echo це друга} #! / Bin / bash echo привіт # виклик першої функції foo # виклик другої функції bar

Майже правильно, за винятком того, що рядок #! / Bin / bash повинна бути першою. Це не важко виправити, але, залишу вам для вправи!

Sed на практиці

Якщо ви вважаєте Sed занадто незрозумілим, не заслуговує на увагу, то ось вам статистика: я порахував кількість використань Sed в системних скриптах Убунту за допомогою самого ж Sed-а:

; find / etc -type f -exec grep -w sed {} \; 2> / dev / null | wc -l

Вийшло 259 прикладів.

У більшості прикладів Sed використовується в командах заміщення для установки значення змінної з вмісту файлу конфігурації, на зразок цього:

pid = $ (sed 's / // g' /var/spool/postfix/pid/master.pid)

У всіх цих прикладах просто видаляються прогалини з вхідного потоку. Ключ g на кінці заміщення говорить Sed-у зробити зміни глобально - всюди в цій певній рядку.

Інший типовий приклад використання Sed-а - взяти значення будь-якої змінної і змінити її певним чином. Приклад з /etc/network/if-pre-up.d/vlan Убунту:

VLANID = `echo $ IFACE | sed "s / vlan0 * //" `

Зверніть увагу на іншу форму записи підстановки команд.

Ось ще один приклад, де спільно працюють Awk і Sed:

arch = `echo" $ line "| awk '{print $ 4}' | sed 's /: $ //' `

Тут Awk вибирає четверту рядок з $ line, а Sed видаляє двокрапки. І, нарешті, шедевр з /etc/bash_completion.d/sysv-rc:

valid_options = ($ (\ tr "" "\ n" <<< "$ {COMP_WORDS [@]} $ {options [@]}" \ | sed -ne "/ $ (sed" s / / \\\\ | / g "<<<" $ {options [@]} ") / p" \ | sort | uniq -u \))

Цей вражаючий шматок скрипта використовує Sed для підстановки команд генерації команди для зовнішньої команди Sed-а. Подумати тільки!..

З мого боку було нечесно приводити цей приклад поза контекстом. Тут невідома структура вхідних параметрів, тому важко сказати, що тут відбувається. По-моєму, найголовніше в розумінні всіх цих уявних потоків, полягає в точному розумінні структури даних, які обробляються на кожному етапі потоку.

Наступного разу я розповім про інше моєму улюбленому малому мовою - Awk. До зустрічі.

Др. Кріс Браун

Бажаєте дізнатися більше?

Офіційне керівництво Sed-а знаходиться на www.gnu.org/software/sed/manual. Тут ви знайдете не тільки докладний довідник по командам, але і всякі мозголомность приклади скриптів для емуляції таких команд, як wc, cat, head, tail і uniq. Тут навіть є скрипт для вирахування чисел, який доводить, що за допомогою Sed-а можна виконувати арифметичні вирази (якщо, звичайно, захочете). Також глава Sed з Unix Power Tools [см. LXF125 стр. 55].

Переклад: Сергій Черепанов

При передруці посилання на unixone.ru обов'язкове.

Чи є Sed мовою?
Чи є Sed мовою?
Складно?

Дополнительная информация

rss
Карта