Стандартный текстовый редактор UNIX создан К. Томпсоном в начале 70-х годов для вычислительной среды на малых машинах (первая система UNIX ограничивала предельный размер программ пользователя до 8К байт) с терминалами "твердой копии", работавшими при очень низких скоростях (10-15 символов в секунду). Этот редактор написан на базе более ранней версии qed, которая была в то время популярна.
С развитием технологии ed постигла та же судьба. Почти наверняка вы найдете в своей системе другие редакторы с интересными свойствами, в частности с возможностью "визуального", или "экранного", редактирования, при котором на экране терминала отражаются все вносимые вами коррективы.
Почему же тогда мы тратим время на, казалось бы, устаревшую программу? Дело в том, что ed выполняет некоторые операции весьма успешно, несмотря на свой возраст. Эта программа есть на всех установках UNIX, и вы всегда найдете ее при переходе с одной системы на другую. Она работает хорошо и с низкоскоростными телефонными линиями, и с любыми терминалами. Кроме того, ed легко запускать из командного файла, в то время как большинство экранных редакторов управляются с терминала и не могут должным образом получить входной поток из файла.
Редактор ed предоставляет регулярные выражения для поиска по образцу. Регулярные выражения, на которых основан ed, присутствуют во всех частях системы: grep и sed применяют почти такие же, a egrep, awk, lex расширяют их. Shell использует для сравнения имен файлов иной синтаксис, но те же самые идеи. Некоторые экранные редакторы имеют "строчный режим", который предусматривает обращение к регулярным выражениям ed.
И, наконец, ed обладает высоким быстродействием. Вполне возможно вызвать ed, заменить в файле одну строку, записать новую версию и вернуться из него, причем все это происходит быстрее, чем один только запуск большого и более сложного экранного редактора.
Основные сведения
Программа ed редактирует один файл за один раз. Она работает с копией файла. Чтобы внести исправления в первоначальный файл, вы должны дать явную команду. Редактор предоставляет команды для манипуляций с последовательными строками или строками, соответствующими образцу, а также команды для внесения в строки изменений.
Каждая команда ed представляет собой символ (обычно букву). Большинству команд может предшествовать один или два номера строки, которые указывают, на какую строку или строки должна воздействовать команда: в противном случае подразумевается номер, принятый по умолчанию. Номер строки можно специфицировать абсолютной позицией в файле (1, 2 ...), символами $ для последней строки и "." для текущей, процедурой поиска по образцу, использующей регулярные выражения, и их аддитивными комбинациями.
Рассмотрим, как с помощью ed можно создавать файлы, используя стихи Де Моргана из первой главы.
$ ed poem
? poem Предупреждение: файл poem не существует
а Начать добавление строк
Great fleas have little fleas
upon their backs to bite 'em,
And little fleas have lesser fleas,
and so ad infinitum.
. Печатаем '.' чтобы закончить ввод
w poem Пишем строки в файл poem
121 ed сообщает, что записан 121 символ
q Выход
Команда а добавляет или присоединяет строки. Режим добавления заканчивается строкой, состоящей из одной точки. Из-за отсутствия индикации режима, в котором вы работаете, возможны две распространенные ошибки: ввод текста без команды а и ввод команды до ввода '.'.
Редактор ed никогда не будет писать ваш текст в файл автоматически; вы должны задать это с помощью команды w. Однако, если вы пытаетесь закончить редактирование без записи ваших изменений, ed выдает '?' как предупреждение. Есть другая команда q, позволяющая завершить работу независимо от внесения исправлений.
$ ed poem
121 Файл существует и имеет 121 символ
а Добавить еще строки в его конец
And the great fleas themselfs, in turn,
have greater fleas to go on;
While these again have greater still,
and greater still, and so on.
. Печатаем '.' для завершения
q Пытаемся выйти
? Предупреждение: не было записи
w Нет имени файла; подразумевается poem
263
q Теперь можно выходить
$ wc poem Проверьте для уверенности
8 46 263 poem
$
Временная передача управления shell с помощью ' ! '
Если вы запустили ed, то можете временно выйти из него, чтобы запустить другую команду shell. В этом случае нет необходимости прекращать работу достаточно ввести команду ed '!'.
$ ed poem
! wc poem Запуск wc без выхода из ed
8 46 263 poem
! Вернулись из команды
q Выход без w годится: не было исправлений
$
Печать
Строки файла нумеруются как 1, 2 … Вы можете печатать n-ю строку, дав команду np или просто номер n, и строки с m по n, используя m,np. "Номером строки" $ обозначается последняя строка, так что строки можно не считать.
1 | Печатать первую строку; 1р то же самое |
$ | Печатать последнюю строку; $p то же самое |
1,$p | Печатать строки с первой по последнюю |
Печатать файл по одной строке проще всего; нажимая клавишу RETURN, вы можете вернуться на одну строку назад с помощью '-'. Можно комбинировать номера строк с '+' и '-'.
$-2,$p | Печатать последние три строки |
1,2+3p | Печатать строки с первой по пятую |
Однако нельзя печатать после конца файла или в обратном порядке; команды типа $,$+1p и $,1p считаются незаконными.
Команда list 1 выводит текст в формате с видимыми символами. Это удобно при поиске в файлах управляющих символов, при различении пробелов, табуляции и т.п. (см. vis в гл. 7).
Образцы
Как только размер начинает превышать две строки, становится неудобным печатать его весь целиком, чтобы отыскать нужную строку. Редактор ed предлагает способ поиска строк, совпадающих с некоторым образцом, шаблоном: /pattern/ обнаруживает очередное вхождение pattern.
$ ed poem 263
/flea/ Ищет очередную строку, содержащую flea
Great fleas have little fleas
/flea/ Ищет еще одну
And little fleas have lesser fleas,
// Ищет следующую по тому же образцу
And the great fleas themselves, in turn,
?? Поиск в обратном направлении по тому же образцу
And little fleas have lesser fleas,
Редактор запоминает образец, применявшийся вами в последний раз, так что можно повторить поиск просто с помощью //. Для поиска в обратном направлении воспользуйтесь ?pattern? и ??.
Поиск с помощью /.../ и ?...? циклический, т.е. продолжается в обратном направлении после достижения одного из концов текста:
$p Печатать последнюю строку ('p' необязательна)
and greater still, and so on.
/flea/ Следующее flea вблизи начала
Great fleas have little fleas
?? От начала идет в обратном направлении
have greater fleas to go on;
Результатом поиска по образцу типа /flea/ является номер строки, например 1 или $, который может использоваться в том же контексте, что и такие номера:
1,/flea/p | Печатать от единицы до следующего flea |
?flea?+1,$p | Печатать от предыдущего flea + 1 до конца |
Текущая редактируемая строка. Редактор ed отслеживает последнюю строку, с которой имели дело: печатали или вводили текст, читали из файла. Это текущая строка с именем '.'. Каждая команда определенным образом влияет на текущую строку, обычно настраивая ее на ту, с которой она последний раз работала. Вы можете использовать текущую строку так же, как $ или номер строки типа 1:
$ ed poem
263
. Печатает текущую строку; после чтения файла
это то же, что $
and greater still, and so on.
.-1,.p Печатает предыдущую строку и еще одну
While these again have greater still,
and greater still, and so on.
Выражения для номера строки могут быть сокращены:
Сокращение | Эквивалент | Сокращение | Эквивалент |
-1 | .-1 | + | .+1 |
-- или -2 | .-2 | ++ или +2 | .+2 |
-n | .-n | +n | .+n |
$- | $-1 | .3 | .+3 |
Добавление, замена, исключение, вставка
Команда а (добавить) добавляет строки после определенной строки, команда d (удалить) вычеркивает строки, команда i (вставить) вставляет строки перед определенной строкой, команда с (заменить) заменяет строки, действуя как комбинация команд "удалить" и "вставить".
na | Добавить текст после строки n |
ni | Вставить текст перед строкой n |
m,nd | Удалить строки с m по n |
m,nc | Заменить строки с m по n |
Если номера строк не указаны, используется текущая строка. Новый текст для команд а, с и i оканчивается строкой '.'; точка, введенная в последней строке, оставляется. Текущая строка настраивается на следующую строку после последней удаленной, за исключением случая, когда удалена последняя строка, т.е. $.
0а | Добавить текст в начало (то же, что 1i ) |
dp | Удалить текущую строку, печатать следующую (или последнюю, если $ ) |
.,$dp | Удалить отсюда до конца, печатать новую последнюю |
1,$d | Удалить все |
?pat?,.-1d | Удалить от предыдущей, совпадающей с ' pat ' до той, что перед текущей |
$dp | Удалить последнюю строку, печатать новую последнюю |
$c | Заменить последнюю строку ($а добавляет после последней строки) |
1,$c | Заменить все строки |
Подстановка, аннулирование
Нет необходимости перепечатывать целую строку, если в ней нужно заменить лишь несколько символов. Команда подстановки s заменяет одну последовательность символов другой:
s/old/new/ | Заменить первую old на new в текущей строке |
s/old/new/p | Заменить первую old на new и печатать строку |
s/old/new/g | Заменить каждую old на new в текущей строке |
s/old/new/gp | Заменить каждую old на new и печатать строку |
Заменяется только самое левое вхождение образца в строке, если не написана буква 'g'. Команда s выводит измененную строку только в том случае, когда она оканчивается буквой 'p'. Фактически большинство команд ed выполняет свою работу "молча", но почти любая команда может быть завершена буквой p для вывода результата.
Если подстановкой вы не добились того, что хотели, с помощью команды u (аннулировать) можно уничтожить последнюю подстановку. Текущая строка должна быть настроена на преобразованную строку:
u | Аннулировать последнюю сделанную подстановку |
up | Аннулировать последнюю подстановку и напечатать |
Как вам уже известно, командам p и d могут предшествовать один или два номера, указывающие строки, на которые нужно воздействовать. Этот же принцип используется и для команды s.
/old/s/old/new/ | Найти следующую old ; заменить на new |
/old/s//new | Найти следующую old ; заменить на new (образец запоминается) |
1 ,$s/old/new/p | Заменить первую old на new в каждой строке; печатать последнюю измененную строку |
1,$s/old/new/gp | Заменить каждую old на new в каждой строке; печатать последнюю измененную строку |
Отметим, что 1,$s вызывает команду для обработки каждой строки, но это означает лишь самое левое вхождение образца в каждой строке; нужна заключительная команда 'g', чтобы заместить все вхождения во всех строках. Кроме того, p выдает только последнюю измененную строку. Для вывода всех измененных строк необходима глобальная команда, которую мы вскоре рассмотрим.
Символ & означает сокращение; оказавшись где либо справа от команды s, он заменяется образцом из левой части:
s/big/very &/ | Заменить big на very big |
s/big/& &/ | Заменить big на big big |
s/.*/(&)/ | Взять в скобки целую строку (см. .* ниже) |
s/and/\&/ | Заменить and на & ( \ отключает специальное значение символа) |
Метасимволы и регулярные выражения
Как и символы *, >, :, имеющие специальный смысл в shell, некоторые символы имеют специальный смысл для ed, если они появляются в образце для поиска или в левой части команды s. Эти символы называют метасимволами, а использующие их образцы регулярными выражениями. В табл. П.1.1 перечислены все символы и их значения. Примеры, приведенные ниже, следует читать в соответствии с таблицей. Специальный смысл любого символа может быть отменен предшествующей ему обратной дробной чертой '\'.
с | Любой специальный символ задает совпадение с таким же символом |
\c | Отменяет специальный смысл символа с |
А | Соответствует началу строки, когда ^ начинает образец |
$ | Соответствует концу строки, когда $ заканчивает образец |
. | Совпадает с любым одиночным символом |
[...] | Соответствует одному любому символу в ... ; допустимы диапазоны типа a-z |
[^...] | Соответствует любому одиночному символу, не входящему в ... ; допустимы диапазоны |
r* | Соответствует нулевому или более числу вхождений r , где r символ, или [...] |
& | Используется только в правой части s; вставляет фрагмент, совпавший с образом |
\(...\) | Помечает регулярное выражение; найденная строка доступна как \1 , и т.д. в левой и правой частях выражения |
Таблица П.1.1: Регулярные выражения редактора
Символу перевода строки не соответствует ни одно регулярное выражение.
Образец | Соответствие |
/^$/ | пустая строка, т.е. только конец строки |
/./ | непустая, т.е. по крайней мере один символ |
/^/ | все строки |
/thing/ | thing где либо в строке |
/^thing/ | thing в начале строки |
/thing$/ | thing в конце строки |
/^thing$/ | строка, состоящая лишь из thing |
/thing.$/ | thing плюс любой символ в конце строки |
/thing\.$/ | thing. в конце строки |
/\/thing\// | /thing/ где либо в строке |
/[tT]hing/ | thing или Thing где либо в строке |
/thing[0-9]/ | thing , за которой одна цифра |
/thing[^0-9]/ | thing , за которой не цифра |
/thing[0-9][^0-9]/ | thing , за которой цифра и не цифра |
/thing1.*thing2/ | thing1 , затем любая строка, затем thing2 |
/^thing1.*thing2$/ | thing1 в начале и thing2 в конце |
Регулярные выражения, использующие *, выбирают самое левое совпадение с образцом до тех пор, пока это возможно. Отметим, что x* может соответствовать нулю, а xx* одному или более символу.
Глобальные команды
Глобальные команды g и v управляют вызовом одной или большего числа других команд, выполняющих преобразования в множестве строк, выбранных регулярным выражением. Команда g наиболее часто используется для печати, подстановки или удаления множества строк:
m , n g/re/cmd | Для всех строк между m и n , которые соответствуют re выполнить cmd |
m , n v/re/cmd | Для всех строк между m и n , которые не соответствуют re выполнить cmd |
Командам g и v могут предшествовать номера строк, ограничивающие диапазон; по умолчанию принимается диапазон 1,$:
g/.../p | Печатать все строки, соответствующие регулярному выражению ... |
g/.../d | Убрать все строки соответствующие ... |
g/.../s//repl/p | Заменить первое вхождение ... в каждой строке на ' repl ', печатать измененные строки |
g/.../s//repl/gp | Заменить каждое ... на ' repl ' , печатать измененные строки |
g/.../s/pat/repl/ | В строках, соответствующих ... , заменить первую ' pat ' на ' repl ' |
g/.../s/pat/repl/p | В строках, соответствующих ... , заменить первую ' pat ' на ' repl ' и печатать |
g/.../s/pat/repl/gp | В строках, соответствующих ... , заменить все ' pat ' на ' repl ' и печатать |
v/.../s/pat/repl/gp | В строках, не соответствующих ... , заменить все ' pat ' на ' repl ' и печатать |
v/^$/p | Печатать все непустые строки |
g/.../cmd1\cmd2\cmd3 | Выполнять составные команды с единственной g , присоединить \ к каждой cmd кроме последней |
Команды, управляемые командами g или v, также могут использовать номера строк, текущая строка настраивается по очереди на каждую выбранную строку:
g/thing/.,.+1р | Печатать каждую строку с thing и следующую |
g/^\.EQ/.1, /^\.EN/s/alpha/beta/gp | Заменять alpha на beta только между .EQ и .EN и печатать измененные строки |
Перемещение и копирование строк
Команда m перемещает группу смежных строк, а команда t копирует группу строк:
m , n ,md | Переместить строки m по n за строку d |
m , n ,td | Скопировать строки m по n за строку d |
Если исходные строки не определены, используется текущая строка. Строка назначения d не может быть в диапазоне m,n-1. Ниже приведено несколько общих идиом, включающих m и t.
m+ | Поместить текущую строку после следующей (переставить) |
m-2 | Поместить текущую строку перед предыдущей |
m-- | То же самое: это то же, что -2 |
m- | Ничего не делать |
m$ | Поместить текущую строку в конец ( m0 — поместить в начало) |
t. | Дублировать текущую строку ( t$ дублирует в конце) |
-,.t. | Дублировать предыдущую и текущую строки |
1,$t$ | Дублировать все множество строк |
g/^/m0 | Инвертировать порядок строк |
Метки и номера строк
Команда = печатает номер строки $ (слабое умолчание), .= печатает номер текущей строки и т.д. Положение текущей строки не изменяется.
Команда kc метит нужную строку буквой с; впоследствии на эту строку можно ссылаться с помощью 'c. Команда k не меняет положение текущей строки. Метки удобны при перемещении больших фрагментов текста, поскольку они остаются привязанными к строкам, как показано в приведенной ниже последовательности:
/.../ka | Найти строку ... и пометить буквой a |
/.../kb | Найти строку ... и пометить буквой b |
'a,'bp | Печатать целый диапазон, чтобы быть уверенным |
/.../ | Найти нужную строку |
'а,'bm | Поместить выбранные строки после нее |
Объединение, расщепление и реорганизация строк
Строки могут быть объединены с помощью команды j (пробелы не добавляются):
m , n j | объединяет строки с m по n в одну |
jp | Объединить текущую строку со следующей и печатать |
-,.jp | Объединить предыдущую строку с текущей и печатать |
По умолчанию принимается диапазон .,.+1;
Строки можно расщепить командой подстановки, отделив новую строку:
s/part1part2/part1\part2/ | Расщепить строку на две части |
s/ /\ /g | Расщепить по каждому пробелу. Оставить одно слово на строку |
Текущей становится последняя созданная строка.
Чтобы манипулировать не только целыми фрагментами, выбираемыми регулярными выражениями, но и их соответствующими частями, используйте помеченные регулярные выражения: если конструкция \(...\) появляется в регулярном выражении, то часть соответствующего ей фрагмента доступна как \1. Возможно до девяти помеченных выражений, на которые ссылаются с помощью \1, \2 и т.д.
s/\(...\)\(.*\)/\2\1/ | Поместить 3 первых символа в конец |
/\(..*\)\1/ | Найти строки, содержащие повторяющиеся смежные цепочки символов |
Команды, работающие с файлами
Командам r и w (читать и писать) могут предшествовать номера строк:
n r file | Читать file ; добавить его после строки n ; текущей становится последняя прочитанная строка |
m , n w file | Писать строки m-n в file ; положение текущей строки не изменяется |
m , n w file | Добавить строки m-n к file ; положение текущей строки не изменяется |
По умолчанию диапазон для w и W (команда W приведена ниже в табл. П.1.2) — это целый файл. Значение n по умолчанию для r равно $, что представляется не очень удачным. Будьте внимательны.
Редактор ed запоминает первое использованное имя файла из командной строки или из команд r, w. Команда f (файл) печатает или заменяет имя запомненного файла:
f | Печатать имя запомненного файла |
f file | Установить запомненное имя на ' file ' |
Команда e (редактировать) вновь вызывает ed с запомненным или новым файлом:
e | Начать редактировать запомненный файл |
e file | Начать редактировать ' file ' |
Команда е защищена тем же способом, что и q: если вы не записали измененную версию, первая команда е выдает сообщение об ошибке; е вновь инициализирует редактор независимо от внесения изменений. В некоторых системах ed связан с е, так что одна и та же команда (е filename) может использоваться внутри и вне редактора.
Шифрование
Файлы могут быть зашифрованы по записи и дешифрованы при чтении с помощью команды x; пароль будет запрошен. Шифрование происходит тем же способом, что и в crypt(1). В некоторых системах команда x заменена на X (прописную букву) во избежание случайностей.
Сводка команд
В табл. П.1.2 перечислены команды редактора, а в табл. П.1.3 допустимые номера строк. Каждой команде предшествует нуль, один или два номера строк, указывающие число используемых строк, если их нет, принимается соглашение по умолчанию. За большинством команд может следовать буква p для вывода последней обработанной строки или 1 для формата списка. Текущей обычно становится последняя обработанная строка; настройка не меняется командами f, k, w, x, =, !.
.а | Выполнять ввод до тех пор, пока не напечатана строка, содержащая только ' . ' |
.,.с | Заменить строки, новый текст заканчивается так же, как для команды а |
.,.d | Исключить строки |
е file | Вновь начать редактировать file . Редактирование начинается даже в том случае, если исправления не записаны |
f file | Запомнить имя файла как file |
1,$g/re/cmds | Выполнить cmds для каждой строки, соответствующей регулярному выражению re; отдельные команды в cmds разделены \newline ( \ +"перевод строки") |
.i | Вставить текст перед строкой; он заканчивается так же, как для команды a |
.,.+1j | .Соединить строки в одну |
.kc | Пометить строку буквой с |
...l | Перечислить строки, делая невидимые символы видимыми |
.,.m line | Переместить строки после строки line |
.,.p | Печатать строки |
q | Выйти. Q выходит, даже если исправления не записаны |
$r file | Читать file |
.,.s/re/new/ | Заменить new на то, что соответствует re |
.,.t line | Скопировать строки после line |
.u | Аннулировать последнюю подстановку в строке (только одну) |
1,$v/re/cmds | Выполнить команды ed cmds для каждой строки, не соответствующей re |
1,$w file | Записать строки в файл; W добавляет (строки к файлу) вместо того чтобы записывать (как новый файл) |
X | Войти в режим шифрования (или ed -х имя_файла) |
$= | Печатать номер строки |
! cmdline | Выполнить команду UNIX cmdline |
(.+1) newline | Печатать строку |
Таблица П.1.2: Сводка команд ed
n | Абсолютный номер строки n, n = 0,1, 2,... |
. | Текущая строка |
$ | Последняя строка текста |
/re/ | Следующая строка, соответствующая re ; после последней $ циклическое движение к первой строке |
?re? | Предыдущая строка, соответствующая re; после первой циклическое движение к последней $ |
'c | Строка с меткой с |
N1+/-n | Строка N1+/-n (аддитивная комбинация) |
N1,N2 | Строки с N1 по N2 |
N1;N2 | Команда: сделать строку N1 текущей, затем вычислить N2. N1 и N2 могут быть определены любым из перечисленных выше способов |
Таблица П.1.3: Номера строк в ed
Упражнение
Если вы думаете, что знаете ed , попробуйте выполнить текст (см. справочное руководство по quiz(6) ).