Глава 1. Краткий учебник про sed.

Содержание

Зачем?
Вступление.
Основные понятия.
История(программа ed).
Где применяется sed.
Итог.
Использование sed.
Как работает sed?
Применение адресных выражений в sed.
sed-скрипты из нескольких команд.
Использование перевода строки для разделение команд, и создание sed-скриптов в файле.
sed и vim
Подавление вывода ключом -n
Регулярные выражения.
Что такое регулярные выражения?
Команда поиска и замены s.

Зачем?

Вступление.

Уж я и не знаю, зачем вы это всё читаете, зато прекрасно знаю, для кого это написано: для тех, кому понадобилось научится применять одну из многих UNIX-Way утилит - GNUsed.

Вообще говоря, sed появилась более 30 лет назад, потому споры о том, что это чисто "линуксоидная" фишка, бессмысленны: существуют версии sed для MS-DOS, Windows, MacOS, да и наверное для всех систем где понятие текст имеет хоть какой-то смысл.

Что такое sed? Это (как вам уже наверняка известно) потоковый текстовый редактор. Текст поступает на обработку не в виде файла как обычно, а в виде потока. По этой причине, sed даже более универсальна, чем все остальные редакторы. Утилита sed может работать без участия оператора, без возможности вывода текста, на системах где вывод файлов вообще не возможен, и даже там, где не существует самих файлов! Достаточно всего-лишь потоков, а уж они есть практически везде.

Основные понятия.

Программы должны уметь обмениваться информацией. Это аксиома. Если программы этого не умеют, это не программы, а набор разбухших монстров, которые еле ворочаются на самых современных компьютерах. Примеры всем известны. Проблема только в том, что для обмена нужно выработать правила взаимодействия, в основном, в каком виде обмениваться? Много лет назад, ещё во времена UNIX, было решено обмениваться исключительно текстовой информацией, и появилось соглашение, что считать текстом, а что - нет. Конечно, многие программы используют какие-то особые, специальные форматы, однако, обмениваться ими в общем случае невозможно (если конечно нет подходящего конвертера спец-формат<->текст). Соглашения следующие:

  1. Текст - это упорядоченный набор строк. Строк может быть любое количество, в том числе их может не быть вовсе.
  2. Строка - это упорядоченный набор символов. Каждая строка заканчивается особым символом '\n'.
  3. Символ - это минимальный неделимый элемент текста.
  4. Алфавитом называется список всех допустимых символов в тексте.

Особый случай - это "чистый текст(plain-text)", алфавит в нём состоит из байтов 32...127, 10, и 9.

Если пользователь, а тем более программист не согласен с этими соглашениями, то у него возникают проблемы.

Замечание

Примеров - масса: я сам, помнится, расстраивался, что у меня "не работает" планировщик - я дописал к его таблице свою строчку, однако планировщик меня тупо игнорировал. Всё просто, не было никакой строчки, была фигня непонятная! Я нарушил пункт 2 - не добавил в конец строки '\n'. Хотя это скорее беда моего редактора, просил-ведь: "сохрани как текст", а он...

Замечание

По поводу последнего предложения: Как и все юзеры, я готов обвинять что угодно, кроме собственной криворукости :-)

Особо отмечу проблемы с текстами в UTF-8. В них символы могут быть в несколько байтов, но это ещё не самое страшное (нас это обычно не сильно волнует, это проблема разработчиков sed, мы можем заметить разве-что сильное уменьшение быстродействия). Самое противное - в "тексте" могут появляться несимволы, что приводит к совершенно неожиданным результатам.

История(программа ed).

Первую свою программу я написал лет 20 назад. Однако историю я знаю ещё глубже - дело в том, что в то время (впрочем как и сейчас) новейший супер-компьютер мне никто не дал, пришлось мучиться на довольно старой технике. Именно по этому я и знаю, какими были тогда текстовые редакторы.

Замечание

Кстати, они и сейчас есть: если у вас любой Linux просто наберите ed. Сразу скажу: что-бы выйти из этого чуда, надо набрать команду q, что впрочем относится ко многим утилитам, в т.ч. и к sed.

Основная (для меня) прелесть Linux заключается в том, что мы можем всё сразу попробовать. Достаточно открыть второе окно (вторую консоль для некромантов), и там набирать команды. ИМХО, если у вас хорошо развита моторная память, вам лучше команды именно набирать, тем-же, у кого более развита зрительная - можно копировать мышкой (кстати, я именно так и делаю).

Внимание

Деструктивных примеров здесь не будет конечно, однако, будьте осторожны: не нужно экспериментировать с правами root'а в каталоге со сверх-ценными данными. Все тестовые файлы могут быть изменены, что намного хуже, чем если-бы они были просто удалены. Посему, создайте каталог для тестов, и уж там развлекайтесь. Я вас предупредил.

Раз уж зашла речь о программах, давайте напишем программу на C.

Пример 1.1. Программа test.c.

#include <stdio.h>

/* comment */

int main()
{
	printf("тестовый файл\n");
	return 0;
}

					


Что делает эта программа не столь важно (если вам интересно - то всё что она сделает будучи скомпилированной - выведет на экран фразу "тестовый файл" и завершит работу), нам интересна эта программа всего-лишь как образец текста. Попробуем отредактировать эту программу так, как это делал я более 20 лет назад:

$ed test.c
90
				

программа ed написала 90 и что-то ждёт. Ну 90 - это размер файла для редактирования, а ждёт наш редактор команды... Первое что хочется сделать - просмотреть файл. Для этого существует команда p, которая выведет нужные нам строки. Какие именно? Это нужно указать явно, по умолчанию будет выведена только последняя строка, записав 1,$p мы выведем все строки нашего файла. Тут 1,$ означает диапазон строк, от первой строки, и до последней (последняя строка обозначается $). Это выражение называется адресным, либо просто - адресом, так-как оно определяет, к каким именно строкам будет применена данная команда.

Замечание

На самом деле, адресные выражения намного более разнообразны (и в sed, и в ed), но для ed я не смог найти выражения, что-бы вывести весь текст сразу. Только через несколько минут до меня дошло, что это просто не нужно - реальные тексты состоят из тысяч строк, потому и нет таких адресов - что-бы всё и сразу. Ну выведется 10000 строк, мы их всё равно не успеем прочитать (парадокс: древние компьютеры работали намного быстрее).

Теперь отредактируем наш файл, к примеру заменим "return 0;", на "return 77;". Как всегда, для этого существует множество путей, наверное самый простой, это заново набрать всю строчку. Команда замены строчек у нас есть, называется она c; однако, нужно ещё и указать, к чему-же её применить (к какой именно строке). Проще всего указать номер этой строки, но его тоже надо узнать (хотя можно и посчитать, но это не наш метод), наберём 1,$n:

1       #include <stdio.h>
2
3       /* comment */
4
5       int main()
6       {
7               printf("тестовый файл\n");
8               return 0;
9       }
10
				

Эта команда сработала так-же как p, только перед каждой строкой вывелся её номер, и теперь ясно, что нам нужно изменить восьмую строчку. Для этого наберём 8c, и теперь наберём строчку по новой, нажмём ENTER и CTRL+C. Просмотрев файл, мы убедимся, что замена выполнена.

Согласитесь, неудобно. Однако, в те далёкие семидесятые годы прошлого века ничего другого придумать было нельзя, текст на экране терминала безвозвратно уползал вверх, и мы видели только последние 24 строки (в 25й вводилась команда). Навигация по тексту как в современных редакторах была невозможна, потому пришлось придумать другие методы. Впрочем тут и придумывать ничего не надо: просто нужно заменить в строке 8 число 0 на число 77. Для этого (кто-бы мог подумать) имеется команда замены. Я долго не понимал, почему эта команда называется s, а не более логично, например replace. Точно это мне неизвестно до сих пор, но думается, что главное в этой команде вовсе не замена, а поиск (search), нам сначала надо найти число 0, и уж затем, если найдём конечно, его заменить. Записывается это просто: 8s/0/77/, т.е. "в восьмой строке нужно заменить 0 на 77".

Замечание

Мне подсказали, почему команда s называется именно так: от слова substitution.

Но на практике и это неудобно - проще было-бы не нам смотреть номер строки с "return", а заставить это сделать компьютер. И конечно это ещё тогда было реализовано: в качестве адреса можно задать не только номер, но и саму строку, или даже часть строки, наберём к примеру /ret/n:

8               return 0;

Как видите, ed самостоятельно находит строку с /ret/, и выполняет для неё заданную команду.

Замечание

Можно задавать диапазоны вроде /begin/,/end/, для всех строк начиная со строки со словом "begin", и кончая строкой со словом "end", либо к примеру /start/,$, что значит: от строки со словом "start" и до конца текста. Например (для нашей программы): /{/,/}/p
{
	printf("тестовый файл\n");
	return 77;
}
					
Если диапазон(ы) не найден(ы), то ed среагирует вопросиком, как на неправильную команду (забегая вперёд, скажу что sed действует по другому).

Где применяется sed.

Редактор ed конечно не самая удобная штука, но на оборудовании 70х ничего лучше придумать было нельзя. К счастью, прогресс не стоит на месте, и с развитием железа развивались и программы для него, в частности текстовые редакторы. Все вы прекрасно знаете, во что превратились сегодня текстовые редакторы - достаточно сказать, что даже самые опытные пользователи попросту не знают 80% возможностей своего любимого редактора! Однако, была и другая ветка развития текстовых редакторов: во многих случаях нет нужды каждый раз писать новые файлы с нуля, часто нужно просто исправлять какой-либо файл по известному алгоритму, это действие конечно можно произвести обычным редактором, но на самом деле, присутствие юзера тут не только не необходимо, но даже вредно! Юзерам свойственно постоянно ошибаться, всё путать да и вообще - юзер, ленивое и капризное существо: если ему было сказано: "сотри в файле %WINDOWS%\sysok.ini все слова hide", а юзер стёр не все, не hide, да и вообще не то(хорошо, если слова в файлах стирал, а не сами файлы), то этот самый юзер будет обвинять меня, вас, глючный компьютер, кривую программу, карму, да что угодно, но только не свою криворукость...

Имеется ещё 1000 и 7 разных применений, где участие юзера нужно если не исключить, то хотя-бы сильно ограничить: например огромный класс задач про безопасность, ну сколько раз можно повторять: "Читай логи!!!", дык пробовал, "ниасилил - многабукв". Правильно, что-бы юзер увидел опасность необходимо отфильтровать весь "мусор", компьютеру всё равно, он железный: было 10482 атаки на закрытую уязвимость, и одна успешная на открытую - в логе будет 10483 строки (в лучшем случае), и даже если юзер - терпеливая и прилежная блондинка, то даже тогда, она скорее сломает PageDown (сотрёт колесо мыши), прежде чем найдёт нужную строчку.

Ну по поводу фильтров можно возразить: дескать в том-же ed можно набрать g/re/p, что приведёт к фильтрации только нужных строк. Та-же функциональность присутствует во всех остальных текстовых редакторах (включая notepad), мало того, для этого и редактор не нужен, можно прямо в консоли набрать grep (ну вы поняли, почему эта команда так названа). Всё это так, но для фильтрации этого зачастую мало, лично мне по любому не осилить ни 10483 не фильтрованные, ни 2174 фильтрованные строки: до того, как я научился использовать sed, мне приходилось составлять зубодробительные конвейеры, а потом всё равно юзать less|PageDown|search, в разных комбинациях. Это не очень сложно, но отнимает массу времени.

В фильтрации, да и не только в ней, часто нужно реализовать очень простой алгоритм, который почему-то не реализован в различных имеющихся утилитах. Писать для этого программу на C лениво да и не быстро. Для таких случаев придуманы скриптовые языки программирования(ЯП), например bash, perl, awk, php, и многие другие. Все они имеют два недостатка:

  1. Они универсальные, а значит тянут за собой множество ненужного функционала. Конечно, это удобно для кодера, но вот пользователю это совсем не нужно. Ему надо просто решение, а не "самый крутой ЯП".
  2. Все эти ЯП жутко медленные, что связано и с п1, и с тем, что каждую команду нужно множество раз "парсить". Вот например bash'евское
    #!/bin/bash
    
    echo $VARIABLE
    						
    Ага. На 10000 строк! Это-ж каждый раз надо прочитать из файла слово "echo", разобраться что оно значит, а потом найти нужную $VARIABLE... Такой скрипт будет выполнятся в сотни раз дольше чем сам вывод!

Таким образом, нам необходим особый ЯП, который лишён этих недостатков. Таким языком как раз и является sed.

Итог.

Компьютеры предназначены для обработки информации, она может представляются в разных видах, но нас интересует только то, что представлено в виде текста, рассмотрим задачи обработки текста в порядке увеличения сложности:
  1. Примитивные задачи.

    С такими задачами справятся доступные утилиты, если нам нужно просто скопировать информацию, или скопировать несколько файлов в один, нам нужна cat, для резки файлов подойдёт split и cut, для поиска подстрок сгодится grep, и так далее.

  2. Задачи, для которых не хватает инструментов из п1

    А вот тут-то и пригодится sed.

  3. Ещё более сложные задачи.

    Если мощности sed не хватает, придётся писать собственный анализатор текста. При этом не стоит забывать про sed - можно сэкономить огромное количество времени (как своего, так и процессорного), если прогнать входные и/или выходные данные через sed-фильтр(ы).

    Подсказка

    Что делать, если мощности и/или возможностей возможностей sed не хватает? Следует выяснить почему так. К примеру: ваша задача требует обработки текста, и ещё чего-то, что в sed не реализовано. У вас есть следующие варианты:
    1. Использовать sed как входной фильтр. К примеру можно не просто посмотреть текст командой less, а посмотреть уже фильтрованный текст.
    2. Вы можете вызвать другую утилиту внутри sed. Это очень мощный, и потому опасный приём. Далее приведены множество примеров с использованием этой возможности.
    3. Вы можете выполнить sed внутри своего приложения. Впрочем, мне не кажется, что это - хорошая идея.
    4. И наконец, вы можете вообще отказаться от sed. Однако, я думаю, очень многие материалы этой книги будут вам полезны, во первых вы сможете быстро и просто протестировать регулярное выражение с помощью sed, а во вторых, речь у меня идёт именно о встроенных в GNU Linux выражениях, а не о каких-то других, которых у вас может и не оказаться под рукой. Вот пример использования регулярных выражений в языке Си (Правда там обсуждается проблема с UTF, но пример как раз и приведён здесь потому, что это проблема касается не только sed, но и всех остальных ЯП).

  4. Недоступные для решения на компьютере задачи.

    Да, есть и такие... Что-ж, вот тут-то и пригодятся обычные редакторы, например этот текст я набирал в kate. Однако, многие части у меня уже были готовые, потому я их прямо суда вставил, прогнав через sed конечно.

Вы можете обсудить этот документ на форуме. Текст предоставляется по лицензии GNU Free Documentation License (Перевод лицензии GFDL).

Вы можете пожертвовать небольшую сумму яндекс-денег на счёт 41001666004238 для оплаты хостинга, интернета, и прочего. Это конечно добровольно, однако это намного улучшит данный документ (у меня будет больше времени для его улучшения). На самом деле, проект часто находится на грани закрытия, ибо никаких денег никогда не приносил, и приносить не будет. Вы можете мне помочь. Спасибо.