Kodomo

Пользователь

Интерактивный режим шелла. Введение в vi

Вместо рассказов про интерактивный режим шелла большая часть этой лекции посвящена текстовому редактору vi. vi предназначен для редактирования простых текстов (т.е. он может быть заменой текстового редактора в FAR или notepad, или idle, но не ворду).

FIXME: Почему я рассказываю про vi, когда есть графические текстовые редаторы – во-первых, потому, что при некотором навыке он позволяет редактировать текст гораздо быстрее, во-вторых, потому, что его знание полезно для интерактивного режима шелла (т.е. для редактирования командной строки и работы с историей), в-третьих, это очень широко распространённый редактор, которым можно пользоваться через ssh.

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

Основы vi

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

FIXME: Основная идея vi: текст больше правят, чем пишут (ср. программы больше читают, чем пишут); правка – поиск места в тексте и замена (т.е. главное, чтобы были удобные команды навигации и удаления текста). vi/vim нужно _учить_.

FIXME: Так у нас по сути редактирование текста распалось на две задачи; гениальная идея vi в том, чтобы под каждую задачу выделить отдельный режим и по-другому интерпретировать в нём клавиатуру; Основных режимов три: normal, insert, command;

FIXME: normal – самый главный режим, режим навигации, копирования и удаления, в нём буквы на клавиатуре трактуются как команды редактирования (e.g. x – удалить букву); из нормального режима переход в другие режимы: i – в insert, : – в command;

FIXME: insert – режим вставки текста, кроме нескольких специальных кнопок, каждая нажатая буква вставляется в текст; command – режим управления самим текстовым редактором, сюда сваливается всё, что нужно делать достаточно редко – с преимуществом, что у команд зато содержательные названия.

FIXME: из любого режима ESC переводит в нормальный (бывают исключения, но редко: Q, q:)

Командный режим

FIXME: vi знаменит тем, что из него даже выйти нетривиально.

FIXME: самые главные команды в command mode: :q – выйти, :w – сохранить файл, (:[w][q][a][!] – [записать][выйти][все окна][игнорировать ошибки]), :h – хелп.

Нормальный режим

FIXME: Нормальный режим – режим перемещения, копирования и удаления. Перемещение – самое главное. В vi неимоверно много команд перемещения. Среди них есть некоторая наиболее полезная база:

Перемещение

FIXME: Стрелки – как обычно. Но лучше использовать не стрелки, а h, j, k, l (т.к. тогда руки не нужно убирать с основной части клавиатуры); стрелки и hjkl работают по логическим строкам – если строка не умещается в экран и перенесена, они прыгают через несколько видимых строк; в vim есть команды перехода по видимым строкам gj, gk; можно им дать более удобные имена, например, :map <Up> gk| :map <Down> gj – сделать, чтобы стрелки ходили по экранным строкам, а hjkl по логическим.

FIXME: перемещение внутри строки; работают Home, End, но дабы не тянуться далеко за ними, для них есть синонимы 0 и $; ещё один полезный способ перемещения – ^ – перейти на первую содержательную букву в строке (т.е. пропустить все пробелы)

FIXME: перемещение по словам: w, e, b, W, E, B

FIXME: поиск; / – поиск вперёд, ? – поиск назад; n – повторный поиск; N – поиск в обратную сторону; в поиске работают регулярные выражения; в vi регулярные выражения чуть-чуть более навороченные, чем в perl, но с немного другим синтаксисом, см. хелпы

FIXME: удобный частный случай поиска: * – поиск слова под курсором, # – оно же в обратную сторону.

FIXME: ещё один удобный частный случай поиска: поиск буквы в строке; tбуква – остановиться не доходя до буквы; fбуква – остановиться на букве; T, F – то же самое в обратную сторону;

FIXME: ещё одна штука, похожая на поиск, очень очень очень полезная: % – перепрыгнуть на парную скобку; если курсор не стоит на скобке, то искать первую попавшуюся открывающую или закрывающую скобку направо от курсора по строке

Ввод текста

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

Редактирование текста

Под редактированием текста мы в первую очередь понимаем удаление частей текста и замену частей текста на другие.

На эту тему в vi придумали следующее: у нас есть хороший развитый язык команд перемещения, мы будем ими обозначать диапазон для нашей команды. И это действие является самым частым, поэтому для него мы используем максимально компактное обозначение: буква команды и за ней буква перемещения. Команды, обладающие такими действиями, называют nesting commands (гнездящиеся команды).

Самые полезные из гнездящихся команд:

Например:

Для гнездящихся команд есть два типичных сокращения:

Т.е.:

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

Copy & paste

Изначально в vi не было возможности отменить действие (undo), и даже когда она появилась, она действовала на одно действие и только в пределах одной строки – поэтому vi старался защитить от потери данных другим способом: всякое удаление текста запоминает удалённый текст. Т.е. в терминах ныне более привычных вам текстовых редакторов команда d (и команда c) выполняет не действие, именуемое delete, а действие, именуемое cut.

Ещё один способ сохранить часть текста – команда yank, о ней мы тоже уже знаем.

В зависимости от того, каким образом мы копировали текст, он будет скопирован либо в построчном режиме, либо в посимвольном режиме. Например, dd и dj копируют текст в построчном режиме, а x и dt? копируют текст в посимвольном режиме.

Вставляется сохранённый текст командами p и P – p вставляет текст после курсора, P – перед курсором. Если сохранённый текст был в построчном режиме, то p и P вставляют текст после и перед текущей строкой соответственно (а не пытаются вставить несколько строк между двух частей текущей строки – как зачастую поступают более привычные вам текстовые редакторы).

vim-only: Для хранения текста в vim есть не одно место (которое в windows называется буфером обмена), а несколько (и называются они зачем-то регистрами). Регистры называются буквами и для обозначения, что под буквой мы имеем в виду регистр, перед ней ставится двойная кавычка. Регистры нужно указывать перед командой, работающей с регистрами:

Кроме регистров, которыми мы можем пользоваться сами, в vim есть несколько регистров, которыми он управляет автоматически. Наиболее значимые из них – регистры "0 .. "9 – в них хранятся последние десять фрагментов, которые мы клали на регистр без указания имени (т.е., например, последние десять строк, которые мы удаляли командами dd и x).

Множитель

Дабы ещё больше выразительной мощности дать командам перемещения (а через них и командам редактирования) в vi перед командой перемещения можно указывать множитель, например:

К последнему примеру нужно отметить, что если в режиме вставки делали какие-нибудь движения курсором, то довольно трудно понять, какое действие вы имели в виду, которое нужно повторить. Поэтому во избежание расхождения мнений о том, что как должно работать повторение будет случаться только в том случае, если в режиме вставки вы делали только наиболее очевидные операции: набирали текст (ну и, может быть, правили его <BACKSPACE>'ом).

Дополнительные способы перемещения

Дополнительные способы редактирования

Дополнительные режимы

На самом деле, в vim режимов сильно больше, чем три:

Режим вертикального выделения можно завершать не только гнездящимися командами, но и командами I и A, и в этом случае соответствующий текст будет добавлен в каждой строке перед началом выделения или после конца выделения соответственно. Здесь нужно ещё заметить, что команда перемещения $ в контексте вертикального выделения имеет другой смысл, чем перемещение на конец строки любым другим способом: она значит именно, что выделение завершается на конце каждой строки.

Макросы

vim-only.

Иногда бывает такое, что команды . не хватает: хочется повторить более чем одну последнюю команду редактирования. Для этого придумали очень простой и очень мощный инструмент – макросы.

Идея такая: почти все команды нормального режима – это обычные буквы, и переходы между режимами – это буквы, и ввод текста в нормальном режиме – это буквы. Всё, что нам нужно – это уметь сказать, что вот у нас где-нибудь лежит последовательность букв, исполни её так, как будто это команды нормального режима (и каких-нибудь ещё режимов, если среди этих команд есть команды перехода из режима в режим).

Делает это команда @. @a обозначает: возьми текст из регистра "a и исполни его как команды нормального режима.

Например, если мы в файле написали текст yyp<C-x> (чтобы ввести C-x в режиме вставки нужно набрать C-v C-x), и мы затащили эту строку в регистр "a (например, сказали 0"aC). И, положим, мы написали в тексте строку 10 little niggers jump on the bed,. Если теперь мы нажмём 10@a, то у нас под этой строкой окажется ещё 10 таких же с уменьшающимся количеством негритят.

Дабы не жонглировать набором текста и запихиванием его в регистр, для макросов сделали специальный режим: команда qa переводит vim в режим записи макроса – мы работаем в vim как обычно в нормальном режиме, но все наши действия записываются в регистр "a. Завершается запись макроса командой q нормального режима.

Т.е. в предыдущем примере мы могли обойтись для записи макроса нажатием qayyp<C-x>q – и тогда бы у нас уже была бы и строка про 9 негритят, и в регистре a был бы макрос для порождения следующей такой строки.

Немножко про командный режим

Многие команды в командном режиме имеют формат :<диапазон><команда><аргументы>; в vi есть много разных способов указания диапазона, нам наиболее интересны три:

Для большинства команд есть сокращения. Вы это уже видели на примере команды :write, которую можно записать как :w

Search & replace

Мы умеем искать текст, вообще говоря, в сочетании с c, n и . это даёт возможность искать и заменять многие вхождения слова, но это всё-таки неудобно.

Специально для поиска есть команда :substitute, которую можно сокращать до :s, которая пишется так: :<диапазон>s/<что ищем>/<на что заменяем>/<параметры поиска>

Ищем, как и прежде, регулярные выражения. В подстановке мы можем использовать содержимое скобок из регулярного выражения ссылками \1 .. \9. \0 и & обозначают целиком найденную строку.

Флагов есть много разных (порядка десяти), наиболее полезных из них два: g – внутри строки заменять все вхождения (по умолчанию в каждой строке диапазона заменяется только первое вхождение); c – спрашивать, уверены ли мы, что хотим заменять текст перед каждой заменой.

Например:

Настройки

FIXME: vi – весьма сложный инструмент и в нём есть много вещей, которые можно настроить на свой вкус; настройки vi делаются командой :set. Без аргументов – показывает текущие настройки.

FIXME: настройки бывают двух типов: флаги и переменные; флаги устанавливаются командой :set флаг и сбрасываются командой :set noфлаг; переменные меняются командой :set имя=значение; в одной команде :set можно поменять много настроек одновременно

Наиболее полезные, на мой вкус, настройки:

Некоторые настройки зачем-то сделаны не в виде настроек, а в виде отдельных команд. Например, чтобы vim подсвечивал разными цветами разные конструкции в файле в зависимости от формата файла, нужно сказать :syntax on (сокращается до :sy on).

Мы можем сохранить те настройки, которые нам нравятся, и мы хотим, чтобы они всегда были такими по умолчанию, в файле ~/.vimrc. vim читает и исполняет этот файл каждый раз в начале работы. Этот файл воспринимается как последовательность команд командного режима (но двоеточие в каждой строке там ставить не обязательно).

Послесловие к vi

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

Если вы действительно хотите много уметь в vim, то нужно хотя бы раз прочитать полность хотя бы первые десять глав документации. Но даже и с этим, традиционна картина, когда встречаются два пользователя vim и с удивлением друг от друга (всегда в обе стороны) узнают много нового об этом странном инструменте.

Я бы не сказал, что это характеризует vim положительным образом.

Интерактивный режим шелла

FIXME: Задачи интерактивного режима в современном шелле: позволять редактировать командную строку, давать доступ к истории (возможность поиска по истории, повторения и правки прошлых команд), помогать набирать текст – дополнять возможные аргументы команд.

FIXME: Наиболее популярный способ это делать – с помощью библиотеки readline(3). Она используется в bash(1) в python(1), в ipython(1), lftp(1) и в большинстве программ, которые предоставляют командную строку. Есть обёртка rlwrap(1), которая делает из любой программы программу с поддержкой редактирования ввода через readline.

FIXME: readline настраивается в ~/.inputrc; echo set editing-mode vi > ~/.inputrc; exec bash; после этой команды редактирование в шелле становится похожим на редактирование текста в vi: межстрочные команды – это хождение по истории, внутристрочные команды – это редактирование строки

FIXME: Наконец, интерактивный режим шелла должен помогать набирать текст команды; для этого мы набираем начало команды и жмём Tab; в bash после 2-й версии автодополнение очень умное, оно наибрает большую базу дополнений в зависимости от того, что уже набрано, это называется bash-completion (можно посмотреть, как это сделано в  /etc/bash_completion{,.d} )

Упражнения

Для не ленивых:

  1. Насколько сдвигать строки определяется переменной 'shiftwidth', e.g. :set shiftwidth=4 или :se sw=4 значит, что отныне команда > будет сдвигать на 4 пробела направо (1)

  2. printf предназначен для форматирования строк так же, как и printf в языке Си и его потомках (C++, php, perl, и т.п.). Первым аргументом у него идёт строка, которую нужно выводить, а последующими -- значения, которые в эту строку нужно подставлять. Таким образом, отличие printf от echo в том, что для printf вся строка должна идти первым аргументом, и ещё в том, что в ней явно должен быть указан перенос строки. (2)