7.3. Навыки работы в vi(1)

Описание:  по умолчанию в системах BSD используется редактор vi(1) многие системные утилиты используют его в своей работе. Кандидат BSDA должен уметь использовать этот редактор: редактировать файлы, редактировать файлы доступные только для чтения, выходить из редактора без сохранения.

Практика: vi(1) including: :w, :wq, :wq!, :q!, dd, y, p, x, i, a, /, :, :r, ZZ, :set number, :set list

Комментарий

Тем, кто сталкивается с vi(1) впервые, может показаться, что программист написавший его был сумасшедшим. Я знаю человека, который утверждал, что при его первом знакомстве с vi(1) он не смог из этого редактора выйти и был вынужден... перезагрузить компьютер reset'ом (ну и кто, спрашивается сумасшедший, программист или пользователь?). vi(1) упомянут в качестве текстового редактора в стандарте POSIX. По этой причине vi(1) есть в любой операционной системе семейства UNIX (принимая во внимание оговорки из следующих двух абзацев). Так что надо сжать зубы и выучить vi(1). Без этого никуда.

Небольшое замечание про vi(1) и vim(1). В мире существует огромное множество клонов редактора vi(1). В стандарте POSIX описаны некоторые свойства редактора vi(1), но конечно не настаивается на том, чтобы люди использовали именно vi(1) и ни что другое. Просто в каталоге /usr/bin/ должно располагаться нечто под названием vi(1), обладающее такими-то свойствами. Пользователям же хотелось, чтобы текстовый редактор как-то развивался, добавлялись новые функции: синтаксическая подсветка, работа с текстами набранными в различных кодировках, фолдинг и многое другое. Потому-то и существует множество клонов стандартного vi(1). Одним из самых удачных и распространённых является редактор vim(1), написанный голландским программистом Брамом Мооленааром (Bram Moolenaar). Название vim расшифровывается как Vi iMproovement т.е. усовершенствованный vi(1). Это чрезвычайно мощное средство редактирования текстов. Если вам кажется, что vim(1) чего-то не умеет, значит вы не дочитали документацию до конца. Весь этот документ, кстати, написан в vim(1). Что особенно приятно, vim(1) портирован во все мыслимые операционные системы (кроме откровенно эзотерических) и везде ведёт себя одинаково.

Текстовый редактор vim(1) написан как полная замена vi(1). В нём есть режим работы полностью эмулирующий vi(1) «за исключением явных ошибок», как пишет об этом Брам. В большинстве дистрибутивов Linux текстовый редактор vi(1) заменён на vim(1). vim(1) запускается под именем vi(1) и входит в vi-compatible режим. Это не единственное место, где авторы дистрибутивов Linux вольно обращаются с утилитами входящими в POSIX. Другими такими примерами являются sh(1) и awk(1), заменённые на bash(1) и gawk(1), но запускающиеся по соображениям совместимости под старыми именами. Во всех BSD системах в каталоге /usr/bin находится оригинальный vi(1). vim(1) доступен только как сторонний продукт и устанавливается из портов.

Кроме vi(1) существует множество более простых редакторов с более или менее очевидным поведением. Большинство из них устанавливается в виде отдельных портов, однако некоторые входят в стандартный комплект и поставляются вместе с операционной системой. Например с FreeBSD поставляется редактор ee(1), написанный в расчёте на то, что с ним сможет работать любой человек, совершенно ничему не обученный. Но все системные утилиты без указания переменной окружения EDITOR будут вызывать именно vi(1).

[Важно]Важно
В данном разделе описан не vim(1), а vi(1) версии 1.79 (10/23/96) The CSRG, University of California, Berkeley. Эта версия сама по себе, обладет некоторыми особенностями отсутсвующими в POSIX-vi. Например, описываемый vi(1) обладает опцией searchinc и возможностью редактирования текста в нескольких окнах (:N).

Основное отличие vi(1) от прочих текстовых редакторов — невозможность набирать текст непосредственно после запуска. Я настоятельно рекомендую новичкам установить порт vim(1) и выполнить команду vimtutor(1). Таким образом, вы познакомитесь с основными клавишами vi(1) и режимами работы. На худой конец, прочтите этот раздел до конца прежде чем приметесь за эксперименты.

vi(1) имеет несколько режимов работы:

Normal mode
Основной режим работы vi(1). В этом режиме нельзя непосредственно набирать текст. В этом режиме осуществляется перемещение по тексту, операции копирования текста в буфер обмена, вставка из буфера и удаления текста в буфер. (Удалить текст не в буфер нельзя, поэтому удаляя текст вы стираете то, что было в буфере до этого.)
Insert mode
Этот режим предназначен для набора текста.
Search mode
Режим предназначен для поиска текста.
Command line mode
В этом режиме можно выполнять разнообразные команды: поиск с заменой, сохранение текста в файл, открыть на редактирование другой файл, выход из редактора установка внутренних переменных в то или иное значение.

7.3.1. Normal mode

Очень важным элементом работы в vi(1) является «движение». Важно уметь передвигаться в vi(1) не посимвольно, при помощи стрелочек, а именно при помощи его буквенных команд, упомянутых ниже. В комбинации с другими командами это даст вам большие возможности по редактированию текста.

Таблица 7.2. Движения в vi(1), Normal mode

КомандаМнемоникаОписание
h j k lнет Это клавиши посимвольного перемещения, тождестенные стрелкам. Стрелки есть не на всех клавиатурах, кроме того, для людей владеющих слепым набором такой способ перемещения удобен, так как им не приходится снимать руки с клавиатуры, для того, чтобы добраться до нужного символа.
wwordПеремещение к началу следующего слова
WWORD Перемещение к началу следующего слова, но под словом подразумевается не совокупность букв, а совокупность непробельных символов. Таким образом, URL http://www.vim.org/ состоит из нескольких слов, но из одного СЛОВА.
eendПеремещение вперёд, к концу слова
EENDПеремещение вперёд, к концу СЛОВА
bbeginПеремещение назад, к началу слова
BBEGINПеремещение назад, к началу СЛОВА
nnext Применяется после поиска в Search mode. Повторить поиск вперёд.
NнетПовторить поиск назад.
}нетПеремещение в конец абзаца
{нетПеремещение в начало абзаца
)нетПеремещение в конец предложения
(нетПеремещение в начало предложения
0нетПеремещение в начало строки
^нетПеремещение к первому символу в строке
$нетПеремещение в конец строки
+нетПеремещение в начало следующей строки
-нетПеремещение в начало предыдущей строки
f<letter>find Перемещение на следующий в строке символ <letter>
F<letter>find Перемещение на предыдущий в строке символ <letter>
t<letter>till Перемещение на позицию предшествующую следующему в строке символу <letter>
T<letter>till Перемещение на позицию следующую сразу за предыдущим в строке символом <letter>
;нет повторить последнее движение заданное клавишами f, F, t, или T
,нетТо же, что ; но в другом направлении
%нет Перемещается на скобку соответствующую той, что под курсором

Перед движением можно вводить числовой префикс. Например: 3} — переместиться на 3 абзаца вниз.

Если перед движением указать некоторое действие, то оно будет проделано с той областью текста, через которую проходит курсор при движении. Например: dw — стереть слово.

Таблица 7.3. Действия в vi(1), Normal mode

КомандаМнемоникаОписание
<move>ddelete Удалить текст, через который осуществлялось движение. При удалении текст помещается в буфер
dddeleteУдаляет целиком текущую строку
Ddelete Удаляет текст от текущего символа до конца строки
<move>yyank Поместить текст через который осуществлялось движение в буфер (не удаляя)
yyyankПоместить в буфер целиком текущую строку
YyankТо же, что и yy, как ни странно
<move>cchange Удалить текст через который осуществлялось движение и немедленно перейти в Insert mode
ccchange Удалить текущую строку и немедленно перейти в Insert mode
Cchange Удалить текст от текущей позиции до конца строки и немедленно перейти в Insert mode
ppasteВставить текст из буфера
PpasteВставить текст из буфера слева от курсора
xнет Удалить символ под курсором. (На заметку: сочетание клавиш xp меняет местами два символа. Почему?)
Xнет Удалить символ слева от курсора (как клавиша BackSpace)
~нет Инвертировать регистр букв (строчная буква меняется на прописную, а прописная на строчную). Например сменить UNIX на unix: 4~
r<letter>нетЗаменить символ находящийся под курсором на <letter>
< <move>нет Уменьшить отступ у текста захваченного движением <move>
> <move>нет Увеличить отступ у текста захваченного движением <move>
Uundo Отменить последнее действие. В vi(1) можно сделать многократное undo, весма нетривиальным способом, на первый взгляд оно кажется однократным[a]. В vim(1) объём undo можно задать любым.
.нетПовторить последнее действие
iinsertВход в Insert mode слева от курсора
IinsertВход в Insert mode в начале строки
aappend Вход в Insert mode справа от курсора (это удобно, если вы переместились в конец слова клавишей e)
AappendВход в Insert mode в конце строки
Rinsert Вход в Replace mode (то же, что Insert, но при наборе символы замещают текст)
oнет Добавить строку под курсором, встать на неё и войти в Insert mode
Oнет Добавить строку над курсором, встать на неё и войти в Insert mode
/нетВход в Search mode для поиска вперёд
?нетВход в Search mode для поиска назад
ZZнетЗаписать файл и выйти

[a] Если вы в vi(1), в Normal mode нажимаете клавишу «u» вы делаете undo. Повторное нажатие приведёт к тому, что вы сделаете undo на undo (т.е. redo). Однако, если вы после нажатия на «u» (undo) нажмёте на точку «.», то вы совершите повтор последнего действия, т.е. ещё одно undo. Таким образом, если вам надо отменить 5 действий, вы должны нажать в Normal mode «u4.». Аналогично можно сделать несколько redo.

Например, если у нас есть надпись <entry>НАДПИСЬ</entry> и нам надо заменить её на <entry>ЬСИПДАН</entry> мы встаём на начало строки и выполняем такое действие: f>lct<ЬСИПДАН правда всё просто и очевидно? Потренируйтесь, обещаю вам, что через неделю вы перестанете понимать как вы до сих пор обходились без vi(1).

7.3.2. Insert Mode

Из Insert mode главное — уметь выходить. Для этого есть миниму два способа: <Esc> или Ctrl+C. Последнее удобнее по эргономичным соображениям.

Вас может удивить, что в vi(1) в Insert mode не всегда можно перемещаться стрелками, не всегда работает BackSpace. Научитесь пользоваться «родными» клавишами vi(1) — их главное преимущество состоит в том, что они работают всегда на всех терминалах, заходите ли вы с чужого макинтоша на тостер под управлением NetBSD или запустили экзотический эмулятор терминала и держите в руках клавиатуру SUN на которой все кнопки другие, а видете её первый (и быть может последний) раз в жизни.

7.3.3. Search mode

Поиск в vi(1) осуществляется либо вперёд, либо назад. Соответственно вход в этот режим осуществляется клавишими / или ?. Выход, так же как и из Insert mode — <Esc> или Ctrl+C.

vi(1) осуществляет поиск при помощи регулярных выражений. В vim(1) их возможности были сильно расширены. В настоящий момент возможности регулярных выражений в vim(1) даже превосходят возможности регулярных выражений в perl(1). К сожалению, работают они значительно медленнее. Но мы рассматриваем не vim(1), а чистый vi(1).

Таблица 7.4. Регулярные выражения в vi(1)

ОператорОписание
.любой символ
[abc]любой символ из указанного набора
[^abc]любой символ отсутствующий в указанном наборе
<atom>* «квантификатор» — vi(1) ищет сколько угодно вхождений <atom'а>
Экранирование последующего символа, например, если нам надо найти текст a* мы должны искать a*, чтобы * не была интерпретирована как квантификатор

Увы, в изначальном vi(1) синтаксис регулярных выражений не был столь развит как в современном vim(1).

В vi(1) FreeBSD можно выставить переменную searchinc (как именно, сказано ниже) и тогда поиск станет инкрементным (т.е. курсор будет перемещаться в процессе набора текста в строке поиска). В vim(1) для этого служит переменная incsearch. По всей видимости, какие-то усовершенствования в vi(1) всётаки вносились...

7.3.4. Command line mode

Некоторые действия можно выполнить в vi(1) только с командной строки. Например задание значения переменной. Вход в командную строку осуществляется из Normal mode клавишей :. Переменные могут принимать числовые значения, а могут выставляться в истину или ложь. В последнем случае они должны задаваться с префиксом no:

  • :set tabstop=8 — приравнять ширину табулятора 8 символам
  • :set tabstop? — Узнать ширину табулятора
  • :set number — включить нумерацию строк
  • :set nonumber — выключить нумерацию строк
  • :set all — вывести список переменных и их значения

Многие команды можно задавать не полностью выписывая имя команды, например :r, :re, :rea и :read это одна и та же команда. Такое имя мы будем записывать так: :r[ead]

Опции тоже могут записываться сокращённо. Таким образом, команды :set nonumber и :se nonu эквивалентны.

Таблица 7.5. Некоторые команды vi(1)

КомандаОписание
:se[t] <option>управляет переменными (см. выше в тексте)
:r[ead] <file> Читает <file> и вставляет его содержимое после курсора
:r[ead]!<cmd> Выполняет в команду <cmd> и вставляет её вывод после курсора
:!<cmd>Выполняет в команду <cmd>
:w[rite] [file] Записать file, если file не указан, записывается текущий файл (т.е. как «кнопка Save»)
:w[rite]! [file] То же, что и :w, но позволяет перезаписать файл открытый в режиме «только для чтения» (например, если вы владелец файла и у вас на него пермиссии r--, vi(1) временно изменит их на rw-).
:q[uit]Выйти из редактора (без сохранения)
:q[uit]! Выйти из редактора (без сохранения) даже если текст был изменён
:wq, :x ZZ Записать файл и выйти из редактора (ZZ в Normal mode)
:wq!, :x! Записать файл даже если он был открыт в режиме «только для чтения» (см. выше) и выйти из редактора
:[range]s[ubstitute]/pattern/subst/[flags] В строках из указанного диапазона искать pattern и заменять его на subst. Если диапазон не указан, замена осуществляется только в текущей строке, если указан флаг g замена производится любое количество раз в каждой строке (иначе заменяется только первое вхождение). Диапазон может указываться в формате n,m (где n и m — номера строк или специальные символы: . — текущая строка, $ — последняя строка в файле). Чтобы произвести замену во всём файле надо указать диапазон 1,$ или %.
:ve[rsion] Напечатать версию редактора vi(1)
:N <file> Разбить окно на две части и открыть файл во втором окне. Переключаться между окнами Ctrl+w. Внимание! Этой функции нет в POSIX vi(1). В vim(1) это делается другим способом. Эта функция есть только в описываемой версии vi встроенной в FreeBSD.

В приведённой ниже таблице перечислены некоторые опции vi(1). если в поле «тип» написано bool, это значит, что переменная имеет логическое значение и устанавливается командой :set option или :set nooption. Если в столбце POSIX стоит «-», то этой опции нет в POSIX-vi, она есть только в описываемой версии vi(1) FreeBSD и с высокой вероятностью в vim(1) эта опция выставляется другим способом.

Таблица 7.6. Некоторые опции vi(1) выставляемые командой :set

ОпцияТипPOSIXОписание
краткаяполная
listlistbool+ Видоизменяет отображение файла таким образом, чтобы пользователь мог отличить пробелы от табуляторов
nunumberbool+Отображает номера строк
tstabstopinteger+задаёт ширину табулятора
swshiftwidthinteger+ задаёт величину отступа добавляемую, например, командой >
searchincsearchincbool-включает инкрементный поиск
rulerrulerbool- Отображает номер строки и колонки в нижней строке экрана
icignorecasebool+Неразличать регистр при поиске
roreadonlybool+ Файл открыт в режиме «только для чтения»
wswrapscanbool+ При поиске, по достижении конца файла, продолжить поиск с начала до курсора. Т.е. искать во всём файле.

Надо честно признаться, я не осветил возможностей vi(1) и на 20%, не говоря уже о vim(1). С другой стороны, объём сведений данных здесь достаточен для повседневной работы и для сдачи экзамена BSDA.


Обновлено: 12.03.2015