- Чи є Sed мовою?
- Заміщення за допомогою Sed
- вибір рядків
- Шаблони і простір утримання
- Приклад зсуву функцій
- Sed на практиці
На цей раз я маю намір розглянути два малих мови, популярних в Лінуксі - 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 мовою?
Складно?