Главная » Программирование для UNIX » Потоковый редактор sed

0

Теперь рассмотрим программу sed. Это будет не очень  сложно сделать, так  как она  является  прямым потомком редактора ed,  знакомство  с ним облегает задачу.

Основная идея  sed очень проста:

$ sed  ‘список команд ed’ имена5файлов

читает построчно входной файл; выполняет  поочередно команды  из списка над  каждой строкой и выводит результат на  стандартное уст-

ройство вывода. Так, можно заменить все вхождения UNIX на UNIX(TM) в группе файлов командой

$ sed  ’s/UNIX/UNIX(TM)/g’  имена5файлов …   >output

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

$  sed  ’…’  file  >file

Для  замены содержимого файла следует использовать либо временный файл, либо другую программу. (Позже мы обсудим программу, реализующую идею перезаписи существующего файла; см. overwrite в главе 5.)

Вывод  каждой строки в sed выполняется автоматически, поэтому нет необходимости  добавлять p после  команды подстановки, –  если это сделать, то каждая измененная строка будет напечатана дважды. В то же время кавычки почти всегда  необходимы, так как многие метасимволы  sed имеют  собственное значение в оболочке. Рассмотрим, например, команду du  –a, печатающую список файлов. Обычно  du выводит размер и имя файла:

$ du –a  ch4.*

18                 ch4.1

13   ch4.2

14   ch4.3

17                 ch4.4

2                   ch4.9

$

Удаление столбца с размерами можно осуществить с помощью sed, но команда  редактирования требует наличия кавычек,  чтобы  не  допустить  интерпретации оболочкой символа * и знака табуляции:

$ du  –a  ch4.*  |  sed  ’s/.*6//’

ch4.1 ch4.2 ch4.3 ch4.4 ch4.9

$

В процессе подстановки удаляются все символы (.*) до последнего спра ва знака табуляции включительно (в шаблоне он показан знаком 6)1.

1        В современных реализациях оболочки пользователя, например в bash, для  помещения в командную строку «настоящего» символа табуляции необхо димо непосредственно перед ним ввести Ctrl5V. Редактор командной строки оболочки csh обычно  использует по умолчанию для введения символов, не требующих интерпретации, этот же управляющий символ. – Примеч. науч. ред.

Аналогичным способом  можно получить имена пользователей и время регистрации из данных, выводимых командой who:

$ who

lr             tty1               Sep  29  07:14 ron           tty3               Sep  29  10:31 you           tty4               Sep  29  08:36 td             tty5               Sep  29  08:47

$  who  |  sed  ’s/  .*  /  /’ lr  07:14

ron  10:31

you 08:36 td  08:47

$

Команда s  выполняет  замену пробела и  всего,  что  за  ним  следует, включая следующий пробел, единственным пробелом. Здесь  тоже  необходимы кавычки.

Практически такая же команда sed применяется в программе getname, возвращающей имя  текущего пользователя:

$ cat  getname

who  am  i  |  sed  ’s/  .*//’

$ getname

you

$

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

Реализация ind чрезвычайно проста – в начало каждой строки добавляется знак табуляции:

sed  s/^/6/’  $*                 ind,  версия 1

Эта версия вставляет табуляцию и в пустые строки, что совершенно излишне. Улучшенная версия основывается на способности sed выбирать строки,  подлежащие изменению.  Если в  начало  команды  добавить шаблон, то будут обработаны только соответствующие ему строки:

sed  /./s/^/6/’  $*         ind,  версия 2

Шаблон /./ определяет строку, содержащую хотя бы один символ, отличный от символа новой  строки; команда s выполняется только для непустых строк. Учтите, что sed выводит все строки независимо от того, вносились ли в них изменения, поэтому пустые строки по-прежнему находятся на своих  местах.

Есть  и другой способ  реализации команды ind. Добавив в начало команды восклицательный знак !, можно обрабатывать только те строки, которые не соответствуют шаблону:

sed  /^$/!s/^/6/’  $*         ind,  версия 3

Шаблон /^$/ определяет пустые строки (конец строки следует сразу  за ее началом), таким образом /^$/! означает «не выполнять команду на пустых строках».

Как  говорилось выше, sed автоматически выводит строки независимо от того,  были  они обработаны или  нет (если  только они не были удале ны).  Кроме  того, можно использовать большинство команд редактора ed. Поэтому не представляет труда  написать программу, которая печа тает первые три (например) прочитанные строки и завершает работу:

sed  3q

Хотя  3q и не является командой ed, она имеет смысл в sed: копировать строки три раза, затем выйти.

Может потребоваться  дополнительная  обработка данных,  например выравнивание.  Для этой  цели  можно было  бы  перенаправить  вывод  sed на  вход  ind, но, поскольку sed способен  выполнять несколько команд одновременно, всю работу  можно выполнить одним  вызовом sed:

sed  s/^/6/ 3q’

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

Может возникнуть желание написать  программу – назовем ее head – для печати первых нескольких строк файлов. Но набрать на клавиатуре sed  3q (или  10q) настолько просто, что, скорее всего,  такую программу  писать нет смысла. Тем  не менее, мы  в своей  системе применяем ind, так как соответствующая ему команда sed несколько сложнее. (В процессе написания мы  заменили 30-строчную программу на С однострочной командой версии 2, рассмотренной выше.) Нет точного критерия целесообразности замены сложной командной строки одной  командой; вот лучшее из правил, которые нам удалось выработать, – поместите новую команду в свой каталог /bin и посмотрите, как часто вы будете ее использовать.

Можно также  поместить команды sed  в файл и  вызывать их  оттуда строкой

$ sed  –f    cmdfile

Выбирать строки можно не только по их номерам, в частности команда

$ sed  ’/шаблон/q’

напечатает строки от начала файла до первой  строки,  соответствующей шаблону, включая и эту строку, а команда

$ sed  ’/шаблон/d’

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

Автоматический вывод результатов, вполне пригодный в большинстве случаев, иногда бывает нежелателен. Он может быть  выключен пара метром –n; в этом случае выводятся только строки, явно  отправленные на печать командой p. Например,

$ sed  –n ’/шаблон/p’

делает то же самое, что и grep. А поскольку условие совпадения может быть инвертировано, то команда

$ sed  –n ’/шаблон/!p’

эквивалентна grep  –v. (Так же как и sed  ’/шаблон/d’.)

Зачем использовать обе команды – sed и grep?  В конце концов, grep  – всего лишь простой частный случай sed. Частично это объясняется историческими причинами –  команда  grep  появилась намного раньше, чем sed. Но grep живет и процветает благодаря своей лаконичности: с ее помощью проще выполнить ту работу, которую обе команды выполняют одинаково. (Команда grep имеет  функции, отсутствующие в sed, например, определяемые параметром –b.) Хотя  бывает, что программы умирают. Была когда-то программа gres, выполнявшая простые подстановки, исчезнувшая почти сразу  после появления sed.

Символы новой строки могут быть добавлены с помощью sed аналогично тому, как это делалось в ed:

$ sed  ’s/$/\

> /’

Эта команда добавляет ко всем  строкам  символ новой  строки, увеличивая вдвое их количество.

$ sed  ’s/[ 6][  6]*/\

> /g’

А эта  заменяет  последовательности пробелов и  символов табуляции одним  символом разделителя строк, помещая каждое слово на отдельной строке. (Регулярное выражение ’[  6]’ соответствует пробелу или  знаку табуляции, а ’[  6]*’ нулю  или  больше тех же символов, так что выражение в целом определяет один  или  несколько пробелов и/или знаков табуляции.)

Можно также использовать пары регулярных  выражений или  номеров строк для  указания диапазона строк, над которыми будет выполняться любая из команд.

$ sed  –n ’20,30p’    Вывести строки с 20 по 30

$ sed  1,10d’             Удалить строки с 1 по 10 (= tail +11)

$ sed  1,/^$/d’        Удалить до первой пустой строки включительно

$ sed  –n /^$/,/^end/p’      Вывести все группы строк, начинающиеся

с пустой строки и заканчивающиеся строкой, начинающейся словом end

$ sed  $d’                   Удалить последнюю строку

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

Отсутствие относительной нумерации строк  является фундаментальным  ограничением программы sed, отсутствующим в редакторе ed. В частности, знаки + и – не обрабатываются в выражениях для  номеров строк, поэтому невозможно вернуться назад во входном потоке:

$ sed  $-1d’                         Недопустимо: ссылки назад запрещены

Unrecognized  command:  $–1d

$

После   того  как  очередная строка прочитана,  предыдущая  исчезает навсегда: нет способа обратиться к строке, «соседней» с текущей, как требует эта  команда. (Если  быть  честными до конца, нужно сказать, что такая возможность имеется, но она требует углубленного изучения команды hold.) Относительная адресация вперед также недоступна:

$ sed  /thing/+1d’           Недопустимо: ссылки вперед запрещены

Зато  есть  возможность выводить данные в несколько файлов. Например,

$ sed  –n ‘/pat/w  file1

>                        /pat/!w  file2′ имена5файлов   

$

выводит строки, соответствующие шаблону pat в file1, а остальные – в

file2. Или, возвращаясь к первому примеру,

$ sed  s/UNIX/UNIX(TM)/gw  u.out’  имена5файлов …  >output

Такая команда, как и раньше, выведет все строки в файл output, но, кроме этого, создаст файл u.out для измененных строк.

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

$ cat  newer

# newer f:  вывести имена  файлов,  созданных после f ls –t  | sed  /^$1$/q

$

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

"/^$1\$/q"

Здесь  вместо  $1 будет подставлено значение, а \$ превратится в $.

Аналогично выглядит программа older, которая выводит имена фай лов, созданных раньше указанного:

$ cat  older

# older f:   вывести имена  файлов,  созданных раньше f ls –tr  | sed  /^$1$/q

$

Единственное отличие заключается в параметре –r команды ls, распо лагающем список в обратном порядке.

Несмотря на  то что  программа sed имеет гораздо больше возможностей,  чем здесь было показано (включая проверку условий, циклы и переходы, запоминание предыдущих строк),  и,  разумеется, выполняет большинство команд редактора ed,  который описан в приложении 1, наиболее часто она применяется именно так, как здесь было описано – с одной-двумя командами  редактирования, без использования длин ных  и  сложных выражений. Основные  возможности  sed  описаны в табл. 4.2, хотя там отсутствуют многострочные функции.

Таблица 4.2. Команды программы sed

Команда         Действие

a\                    дописывать в выходной файл строки, заканчивающиеся знаком \ b label               переход к метке label

c\                   заменить строки указанным текстом, в котором строки заканчиваются знаком \

d                        удалить строку и прочитать следующую из входного потока

i\             вставить текст перед продолжением вывода

l              вывести строку с отображением непечатных символов

p     вывести строку

q     выйти

r file                  читать файл file, копировать содержимое в выходной поток

s /old/new/f  заменить фрагмент old фрагментом new. При  f=g заменить все, при f=p печатать, при f=w file писать в file

Таблица 4.2 (продолжение)

Команда

Действие

t label

w file

y /str1/str2/

=

!cmd

: label

{

проверка: перейти к метке label, если  в текущей строке выполнена подстановка

вывести строку в файл file

заменить все символы строки str1 соответствующими символами строки str2 (диапазоны недопустимы)

вывести номер  текущей строки

выполнить команду cmd, только если строка не выбрана установить метку label для команд b и t

сгруппировать команды до следующего символа }

Программа sed удобна  тем,  что может обрабатывать файлы произвольной  длины, работает быстро  и имеет  много  общего  с редактором ed, в том числе  регулярные выражения и построчную обработку. С другой стороны, к ее недостаткам можно отнести ограниченную возможность запоминания (тяжело переносить текст из одной строки в другую), обработку данных только за один проход, невозможность перемещения назад, отсутствие ссылок вперед (/…/+1) и отсутствие средств для  вычислений – это всего лишь текстовый редактор.

Упражнение 4.5. Измените программы older и newer так, чтобы  они не выводили имя переданного им файла. Измените порядок вывода на обратный. ~

Упражнение 4.6.  Используйте sed,  чтобы  повысить надежность программы bundle. Подсказка: при использовании встроенных документов маркер конца распознается только при точном соответствии строке. ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии