Введение в POSIX'ивизм

       

||' Filename Вывод команды grep


Весьма часто при обработке текстов встает такая задача: заменить одно слово (или последовательность слов) на другое сразу во многих файлах. Как она решается "подоконными" средствами? Обычно - открытием всех подлежащих изменению документов в word-процессоре и применением функции поиска/замены последовательно в каждом из них.

Таким же способом можно воспользоваться и в POSIX-мире. Это просто, но уж больно скучно. Тем паче, что здесь есть очень эффективная альтернатива - средства потокового (неинтерактивного ) редактирования, примером которых является sed.

Потоковое, или неинтерактивное, редакторование не требует загруки документа в память (то есть открытия), как в обычных текстовых редакторах и word-процессорах. Нет, при нем подлежащий изменению файл (или группа файлов) обрабатываются построчно с помощью соответствующих команд, задаваемых как опции единой командной директивы. В наши дни это выглядит анахронизмом, однако в ряде случаев оказывается чрезвычайно эффективным. Каких? - ответ легко дать на нескольких конкретных примерах.

Так, при установке некоторых дистрибутивов Linux из числа Source Based вполне возможна ситуация, когда в какой-то момент времени в распоряжении пользователя не окажется никакого обычного текстового редактора. А необходимость внесения мелких изменений в конфигурационные файлы (например, в файл /etc/fstab) - возникнет. Так что делать - бежать срочно устанавливать свой любимый vim или emacs? И то, и другое - дело отнюдь не пяти минут. И тут самое время вспомнить про sed, который обязательно будет присутствовать в системе (поскольку он используется во многих установочных сценариях базовых пакетов).

Другой случай - во многих десятках, а то и сотнях, файлов требуется изменить одну-единственную строку, причем - одинаковым образом (например, заменить копирайт Васи Пупкина на Петю Лавочкина). Неужто для этой цели нужно вызывать мощный текстовый редактор, грузить в него немерянное количество документов, перемещаться тем или иным способом перемещаться к нужному фрагменту, вносить требуемое изменение? Отнюдь, ибо sed поможет и здесь, позволив выполнить изменение любого количества файлов в один в пакетном режиме.


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

В самом общем виде sed требует двух аргументов - указания встроенной его команды и имени файла, к которому она должны быть применена. Впрочем, в качестве аргумента можно задать только простую команду, мало-мальски сложное действие (а команды поиска/замены принадлежат к числу сложных) необходимо определить через значения опции -e, заключенные в кавычки (одинарные или двойные - по ситуации). Что же касается имен файлов - то их в качестве аргументов можно указать сколько угодно, в том числе и с помощью масок типа *, *.txt и так далее. Правда, sed не обрабатывает содержимое вложенных подкаталогов, но это - дело поправимое (как - скоро увидим). Так что поиск и замена слова или их последовательности выполняются такой конструкцией:

$ sed -e 's/Вася Пупкин/Петя Лавочкин/' *

Здесь s - это команда поиска, Вася Пупкин - искомый текст, а Петя Лавочкин - текст для замены. В приведенной форме команда выполнит поиск и замену только первого вхождения искомого текста. Чтобы заменить текст по всему файлу, после последнего слэша (он обязателен в любом случае, без него sed не распознает конца заменяющего фрагмента) нужно указать флаг g (от global). Важно помнить, что если оставить заменяющее поле пустым, искомый текст будет просто удален.

По умолчанию sed выводит результаты своей работы на стандартный вывод, не внося изменений в файлы аргументы. Так где же здесь редактирование? Оно обеспечивается другой опцией - -i, указание которой внесет изменения непосредственно в обрабатываемый файл.В результате команда для замены, например, всех вхождений html на shtml во всех файлах текущего каталога будет выглядеть так:

$ sed -i -e 's/html/shtml' *

А что делать, если таким же образом нужно обработать файлы во всех вложенных подкаталогах? Придется вспомнить об универсальной команде find, описанной . В форме

$ find . -name * -exec sed -i -e 's/html/shtml' * {} \

она с успехом справится с этой задачей.

Я привел лишь элементарные примеры использования sed. На самом деле возможности его много шире - представление о их применении в реальных ситуациях можно получить из специальной статьи Сергея Майкова.


Содержание раздела