Всем привет, с вами еженедельная рубрика про Вим!
Мы уже обсуждали диапазоны строк, к которым можно применять команды: копировать, перемещать, удалять, сортировать, делать замену, сохранять в файл, сортировать и многое другое.
Диапазон можно задавать по номеру: 42,48sort или .,$s/1/2/, по смещению: -3,+4m0, или по шаблону: /subroutine/,/end\s*subroutine/s/x/y/g
Но все эти способы задают сплошной диапазон: от первой указанной строки до последней.
А иногда надо выбрать строки и только к ним применить команду. Например, удалить пустые, или заменить две и более пустых строк на одну, или выбрать строки, содержащие шаблон, и в них сделать замену.
Вот пример: вы хотите заменить переменную start на begin, но не хотите трогать текст в комментариях, которые начинаются с данного символа (с !, например) и идут до конца строки.
Для этой цели существуют фильтры строк:
:global (сокращенно :g) и :v, также известный как :g!
Фильтр :g использует шаблон для отбора строк, а :v отбирает строки, не подпадающие под шаблон. А после шаблона указываете команду, как будто у вас диапазон строк. Примеры:
:g/error/m0 — переместить сообщения об ошибках наверх.
:g/^\s*$/d — удалить пустые строки.
:v/\S/d — или так: если нет непробельных символов, то строка пустая.
:g/^\(\s*\n\)\{2,}/d — пример похитрее: удалить серии пустых строк, от двух и более, заменив на одну.
:v/^\s*!/s/\<x\>/y/g — в строках, которые не начинаются с восклицательного знака, заменить все x на y.
А вот жизненный пример: при преобразовании списка литературы в формат BiBTeX часто надо авторов не через запятую, а через and. Ну и:
:g/author\s*=/s/,\s\+/ and /g — в строках, где есть "author =", заменить запятую с пробелами после нее на and.
:g/love/ — вывести все строки про любовь
:g/spy/nu — вывести строки про шпионов с номерами
:g/\d/normal <C-A> — увеличить первое число на единицу в тех строках, где числа есть. Команда normal позволяет выполнить команду обычного режима, как если бы вы вручную находили строки и нажимали соответствующие клавиши. В данном примере надо физически нажать control+A — так, как если бы хотели бы применить эту команду (инкремент).
Фильтр проходит по диапазону, если он указан (по умолчанию весь файл), помечая совпадающие строки, а потом применяет к каждой операцию. Поэтому сохранить в файл или сортировать так не получится.
Если на какой-то строке команда не может выполниться, работа не прекращается, обрабатываются прочие строки.
Отмена (u) и повтор (<C-R>) отменяют или повторяют сразу весь пакет, а не по одной строке. При замене вы можете использовать модификатор /c для подтверждения каждой замены. Например,
:g/in these lines/s/42*/666/gc — в строках, содержащих данный текст заменить все вхождения 4, 42, 422 и т.п. на 666 — но с подтверждением каждой замены.
Как я уже сказал, фильтр может применяться к диапазону (но не к фильтру). Это удобно, ведь порой в одном файле может быть два совершенно разных текста: разные главы, разные функции, разные таблицы с данными.
Вот пример, команды, которую я активно использую: в .vimrc задана горячая клавиша:
:map <C-D> .,.g/^\s*$/d<CR>
Нажимая control+D, я вызываю команду-фильтр:
.,.g/^\s*$/d
Указан диапазон (.,.): от текущей строки до текущей строки: то есть фильтр применяется только к одной строке, на которой курсор. Шаблон: на строке только пробелы, то есть строка должна быть пустой. Все строки (то есть, только одна текущая), удовлетворяющие шаблону, удаляются.
Таким образом, <C-D> удаляет текущую строку, если она пустая. Это очень удобно, когда редактируешь чужой текст, и хочешь грохнуть лишние пустые строки, но dd использовать страшно: а вдруг на строке что-то есть, или промахнулся?
На самом деле, я еще убираю выделение того, что искали:
:map <C-D> .,.g/^\s*$/d<CR>:noh<CR>
Единственный минус: выделение того, что искал да применения команды, пропадает. Можно вернуть, но не вижу нужды.
Если файл большой, работа может затянуться. <C-C> должен помочь, прервать работу команды.
Все на этом. Удачи!