digiwhite 23.11.2010 17:47

ПереводыSed в примерах. Часть 1

Оригинал
Познакомьтесь с мощным текстовым редактором из мира UNIX.


Выбор редактора

В мире UNIX, когда речь заходит о редактировании текста, появляется множество возможностей. Смотрите сами - vi, emacs и jed сразу приходят на ум, наравне со множеством других редакторов. У каждого из нас есть любимый редактор (как и любимые клавиатурные комбинации), который мы хорошо освоили и полюбили. С его помощью мы с легкостью справимся с любым объемом заданий, связанных администрированием UNIX или программированием.

Несмотря на то, что интерактивные редакторы сами по себе хороши, у них есть и ограничения. Их интерактивная природа может быть как преимуществом, так и недостатком. Рассмотрим ситуацию, когда вам надо совершить аналогичные изменения в группе файлов. Вы можете инстинктивно запустить ваш любимый редактор и выполнить руками кучу однообразных, повторяющихся и затратных о времени действий. Но есть выход получше.


Добро пожаловать в sed

Было бы здорово, если бы мы могли автоматизировать процесс редактирования файлов, чтобы вносить "групповые" изменения в файлы, или даже писать скрипты для "тонкого" изменения существующих файлов. К счастью, у нас для таких ситуаций есть лучшее решение. И имя ему sed.

Sed - это легковесный потоковый редактор, имеющийся практически во всех разновидностях UNIX, включая Linux. У sed есть много замечательных возможностей:
Он очень "легкий". Скорее всего, во много раз легче, чем ваш любимый скриптовый язык.
Из-за своей потоковой природы, sed может выполнять операции над данными, полученными из стандартного потока ввода (stdin), например, перенаправление вывода какой-либо команды. Таким образом, вам даже не нужно сохранять текст на жестком диске для редактирования. Т.к. данные могут быть легко переданы sed во входном потоке, очень легко использовать sed как часть длинной и сложной команды с перенаправлениями потока(pipeline) в мощных shell скриптах. Попробуйте сделать подобное с помощью вашего любимого текстового редактора.
GNU sed

К счастью для пользователей Linux, GNU sed - одна из самых мощных версий sed. Каждая сборка Linux имеет в своем арсенале GNU sed, или, по крайней мере, должна. GNU sed пользуется популярностью не только потому, что его исходные коды открыты, но и потому, что он обладает полезными и экономящими время расширениями по сравнению с POSIX sed. Помимо всего прочего GNU sed не страдает от ограничений, присущих предыдущим и проприетарным версиям sed, например, таких как ограничение на длину строки команды - GNU sed с легкостью справляется с командами любой длины.


Правильный sed

В данной статье мы будем использовать GNU sed. Однако, некоторые (всего несколько) из большинства продвинутых примеров, которые приведены далее в статье, не будут работать с GNU sed версии 3.02 или 3.02a, им потребуется более свежая версия. Если вы используете не GNU sed, то ваши результаты могут отличаться от моих. Теперь, почему бы нам не потратить некоторое время на установку GNU sed (см. дополнительные ресурсы)?. После этого вы будете готовы не только к продолжению чтения и выполнения примеров, но и получите возможность использовать, пожалуй, лучший среди всех когда-либо существовавших вариантов sed.


Примеры

Sed работает путем формирования некоторого набора операций для редактирования, указанных пользователем (назовем ее - команда), над входными данными. Sed работает построчно, т.е. команда выполняется над каждой строкой в порядке их поступления. И затем sed записывает результат преобразования в стандартный поток вывода (stdout); при этом модификация исходных данных (например, входного файла) не происходит.

Теперь давайте посмотрим некоторые примеры. Первый выглядит немножко странно потому, что в большей степени я использовал его только для того, чтобы показать как работает sed, нежели чтобы выполнить какую-то полезную работу. Однако, если вы новичок в работе с sed, то очень важно, понять что такое sed. Итак, наш первый пример:

$ sed -e 'd' /etc/services

Если вы выполните эту команду в окне терминала, то получите абсолютное "ничто". Однако, что же сейчас произошло? В данном примере мы вызвали sed, указав ему одну команду для редактирования - 'd'. Sed открыл файл /etc/services, прочитал строку в свой внутренний буфер, выполнил нашу команду ("удалить строку" - от delete line) и затем вывел содержимое буфера (который стал пустым после выполнения команды). Затем эта операция повторилась для следующей строки. Затем еще для одной и так до тех пор, пока sed не добрался до конца файла. Таким образом, команда 'd' затирает каждую строку в буфере sed!

Однако есть еще пара вещей, которые стоит сказать о данном примере. Во-первых, /etc/services не был изменен. Повторюсь еще раз, так происходит потому, что sed читает данные из файла в свой буфер, который вы ему указали и использует их - sed не пытается модифицировать сам файл. Во-вторых, sed работает только с одной строкой за раз. Команда 'd' не говорит sed удалить все входные данные за один раз. Вместо этого sed читает строку за строкой в свой внутренний буфер, называемый "буфер шаблонов". Когда строка прочитана и помещена в буфер, sed выполняет команду над его содержимым и печатает результат (в нашем примере это пустая строка). Позже я покажу вам, как использовать диапазоны адресов в случае, если необходимо выполнить команду только для определенных строк. Если же диапазон не указан, то команда выполняется для всех прочитанных строк.

В-третьих, нужно сказать об использовании одинарных кавычек, обрамляющих команду d. На самом деле, это хорошая идея - взять себе за привычку заключать в одинарные кавычки команды, которые вы задаете sed. Это облегчит работу вашему командному интерпретатору, т.к. он не будет пытаться трактовать их как команду для себя.


Ещё один пример

Теперь рассмотрим пример, который используется для удаления первой строки файла /etc/services в нашем выходном потоке:

$sed -e '1d' /etc/services | more

Как вы видите, эта команда очень похожа на команду из нашего первого примера, за исключением того, что у нее есть префикс в виде цифры 1. Если вы предположили, что единица указывает на строку с номером один, то вы правы. Если в первом примере мы просто использовали команду d, то в этот раз мы предваряем ее необязательным числовым адресом. Используя адреса, вы можете сказать sed, над какой строкой или строками конкретно необходимо выполнить команду.


Диапазоны адресов

Теперь давайте посмотрим, каким образом можно указать диапазон адресов. В данном примере sed удалит строки с первой по десятую:

$ sed -e '1,10d' /etc/services | more

Когда мы разделяем два адреса запятой, sed считает, что следующая за адресами команда должна быть применена к диапазону строк, при этом начальной соответствует первый адрес, а конечной второй. В вышеприведенном примере команда d была применена к строкам с первой по десятую включительно. Все остальные строки были проигнорированы.


Адресация с помощью регулярных выражений

Теперь пришло время для более полезного примера. Скажем, вы хотите посмотреть содержимое файла /etc/services, но вас совершенно не интересуют комментарии, находящиеся в нем. Вы знаете, что можно добавлять комментарии в файл /etc/services, начав новую строку с символа '#'. Соответственно, для того, чтобы не просматривать комментарии мы можем указать sed удалять строки, которые начинаются с символа '#. Далее показано, как сделать это:

$ sed -e '/^#/d' /etc/services | more

Запустите этот пример и посмотрите, что произошло. Вы увидите, что sed блестяще выполнил поставленную задачу.

Для того чтобы понять, что же значит команда '/^#/d' нам придется ее "препарировать". Для начала давайте удалим операцию 'd' - мы уже использовали ее ранее для удаления строк. Новая часть '/^#/' является адресом, представленным регулярным выражением. Регулярное выражение всегда обрамляется символом '/' (слеш). Оно определяет шаблон текста, и команда, следующая сразу за адресом, представленным регулярным выражением, применяется только к строке, совпадающей с шаблоном.

Итак, '/^#/' - это регулярное выражение. Однако, что это значит? Очевидно, это значит, что пришло время рассказать о регулярных выражениях больше.


Регулярные выражения, дополнение

Мы можем использовать регулярные выражения для явного обозначения шаблонов, которые можно найти в тексте. Если вы когда нибудь использовали символ '*' в командах командного интерпретатора, значит, вы использовали нечто похожее, но не идентичное регулярным выражениям. Ниже я описал специальные символы, которые могут быть использованы для конструирования регулярных выражений.
СимволОписание
^Начало строки
$Конец строки
.Любой символ
*Ноль или более совпадений с символом, предваряющим *
Совпадение с любым из символов, заключенным в < >

Вероятно, лучший способ понять, что такое регулярные выражения -- это посмотреть несколько примеров. Все примеры могут быть использованы в качестве левой части команды sed для указания адреса. Итак:
Регулярное выражениеОписание
/./Совпадение с любой строкой, содержащей хотя бы один символ.
/../Совпадение с любой строкой, содержащей хотя бы два символа.
/^#/Совпадение с любой строкой, начинающейся с символа '#'.
/^$/Совпадение с любой пустой строкой.
/}$/Совпадение с любой строкой, которая заканчивается символом '}' (без завершающих пробелов).
/} *$/Совпадение с любой строкой, которая завершается символом '}' за которым следует ноль или более пробелов.
//Совпадение с любой строкой, которая содержит любой из следующих символов в нижнем регистре: 'a', 'b' или 'c'.
/^/ Совпадение с любой строкой, которая начинается с любого из следующих символов в нижнем регистре: 'a', 'b' или 'c'.
Я настоятельно рекомендую вам попробовать некоторые из этих примеров. Уделите изучению регулярных выражений немного времени, попробуйте написать несколько выражений сами. Можно использовать регулярное выражение вида "regexp" следующим образом:

$ sed -e '/regexp/d' /путь/к/моему/тестовому/файлу | more

Таким образом sed удалить любую строку, которая совпала с шаблоном. Однако, в случае с регулярными выражениями бывает легче указать sed выводить совпадения с шаблоном, а не удалять совпадения. Для этого есть следующая команда:

$ sed -n -e '/regexp/p' /path/to/my/test/file | more

Обратите внимание на новый параметр '-n', который не дает sed выводить буфер шаблона, пока не будет явно указано. Также заметьте, что мы заменили команду `d` на `p`, которая, как вы уже догадались, явно указывает sed, что нужно выводить буфер шаблона. Вуаля, теперь будут выводится только совпадения с шаблоном.

Ещё про адреса

На данный момент мы рассмотрели способы адресации к строке, к диапазону строк и адресацию с помощью регулярных выражений. Однако, здесь еще есть место для расширения возможностей. Можно указать два регулярных выражения, разделенных запятой. Тогда sed будет действовать следующим образом: выведет все строки, начиная с совпадения с первым регулярным выражением, до тех пор, пока не будет найдено совпадение со вторым регулярным выражением. Например, следующая команда напечатает текстовый блок, который начинается со строки, содержащей слово "BEGIN" и заканчивается строкой, содержащей слово "END":

$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more

Если "BEGIN" не найдено, то вывода не будет. Если же "BEGIN" найдено, но ниже ни в одной строке не встречается "END", то будут напечатаны все последующие строки. Это происходит из-за потоко-ориентированной природы sed — она не знает, встретит еще "END" или нет, и поэтому выводит весь текст, пока не найдет нужное слово.

Пример: исходный код на языке C

Если вы хотите вывести только функцию main() из файла с исходным кодом на языке C, наберите:

$ sed -n -e '/main<<:space:>>*(/,/^}/p' sourcefile.c | more

Эта команда содержит два регулярных выражения, '/main<<:space:>>*(/' и '/^}/', и одну команду `p`. Первое регулярное выражение совпадает со строкой "main", после которой может следовать сколько угодно пробелов или табуляций, заканчивающихся открывающей скобкой. Это должно совпадать с началом большинства ваших объявлений функции main() в ANSI C.

В этом особом регулярном выражении появился символьный класс, обозначаемый как '<<:space:>>'. На самом деле, это просто ключевое слово, которое сообщает sed, что необходимо искать совпадение с символами пробела или табуляции. Однако, если угодно, то вместо использования '<<:space:>>' можно написать следующее: ввести символ '<', затем вставить пробел, затем нажать комбинацию Control-V, далее нажать кнопку табуляции и в конце ввести '>'. Комбинация Control-V указывает командному интерпретатору, что вы действительно хотите вставить символ табуляции в строку, а не пытаетесь использовать возможность завершения команды. Но все же лучше использовать ключевое слово '<<:space:>>', особенно в скриптах.

Отлично, теперь пришло время поговорить о втором регулярном выражении. Регулярное выражение '/^}/' будет совпадать с символом '}', который находится в начале строки. Если у вас хорошо оформленный исходный код, то это регулярное выражение совпадет с закрывающей фигурной скобкой функции main(). Если же это не так (у вас все же "ужасно оформленный" исходный код, вы не соблюдаете отступы и закрывающая фигурная скобка функции main() находится не в начале строки), тогда написание регулярного выражения для поиска окончания функции является нетривиальной задачей.

Команда p делает то же, что и всегда -- ясно дает понять sed, что необходимо печатать строки, соответствующие заданным регулярными выражениям с учетом того, что мы находимся в "молчаливом" '-n' режиме. Попробуйте выполнить вышеописанную команду над каким-либо исходным файлом с текстом программы на языке C. Результатом выполнения должен стать вывод функции main() в месте с ее телом, включая "main()" и закрывающую фигурную скобку '}'.

Анонс следующей части

Итак, теперь мы познакомились с основами и накопили немного знаний для понимания следующих двух статей. И если вы в настроении для поглощения новой информации о работе с sed, то наберитесь - следующие статьи на подходе! Тем временем, пока я занят их написанием, предлагаю вам посмотреть в Интернет и других источниках информацию, связанную с sed и регулярными выражениями.

Дополнительные ресурсы
Наш перевод статьи "Sed в примерах. Часть 2" и "Sed в примерах. Часть 3".
Другие статьи Реда Дэниэлса на Fantoo: "Sed в примерах. Часть 2" и "Sed в примерах. Часть 3".
Замечательный FAQ по sed от Эрика Пимента.
Исходные коды sed на ftp://ftp.gnu.org/pub/gnu/sed.
Список удобных однострочных sed скриптов от Эрика Пимента, который должны посмотреть те, кто стремиться стать гуру sed.
Если вам нравятся хорошие старомодные книги, то книга O'Reilly's sed & awk, 2nd Edition будутет отличным выбором.
Посмотрите how-to по регулярным выражениям на сайте python.org.
Ссылка на обзор по регулярным выражениями от Университета Кнтуки.
Оригинал (английский)
Переведено при помощи сервиса translated.by инициативной группой переводчиков welinux при участии пользователей digiwhite, dr_lo, settler,Zereal, dpwiz, blackraven, alff31 и Transmitter

http://translated.by

Продолжение следует...


Тэги: editors regexp sed редактор ругулярные выражения
+ 28 -
Похожие Поделиться

_DM_ 23.11.2010 18:24 #
Спасибо, это интересно!
nemckoff 23.11.2010 18:46 #
more?
tru-линуксоиды используют less, а ещё лучше скрипт от vim, который ещё и синтаксис покрасит =)
krig 23.11.2010 19:00 #
Стоило опубликовать этот топ на одни сутки раньше, и было бы вобще великолепно =)

Буквально вчера вечером помогал сестре (дыа, завидуйте!) разобраться с мааааленьким таким выражением на седе. Догадайтесь (не выполняя, чтобы было интересней) что оно делает:
sed '$!N;s/\n/ /'
saver 23.11.2010 21:05 #
join?
krig 23.11.2010 21:08 #
Почти.
philosoft 23.11.2010 22:04 #
Соединяет каждую пару строк.
krig 23.11.2010 22:13 #
Если ответил до того как проверил примером - молодец =)
philosoft 23.11.2010 22:23 #
Да. Читал ман на опеннете пару недель назад, нужна была такая штука. ?
NutipA 23.11.2010 23:44 #
Что касается переводных статей, то ребята мало того, что делают отличный перевод, так еще и умудряются выбирать очень достойный материал из всего интернета. А с некоторыми статьями, так вообще угадывают настолько, что она приходит в ньюсфид именно перед тем, как тебе нужно сделать что-либо по теме. В общем, спасибо большое сообществу!
ant 24.11.2010 11:34 #
Очень большое спасибо за статью!