Найти в Дзене
Блокнот математика

Рубрика "Секреты Вим". Фильтры строк

Обсудим команду, которая позволяет обработать сразу много строк, поддпадающих под шаблон (или не подпадающих под него). Найти, сделать замену, переместить, удалить... Это вроде несплошного диапазона - вещь очень полезная!

Всем привет, с вами еженедельная рубрика про Вим!

Оглавление рубрики

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

Диапазон можно задавать по номеру: 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 — так, как если бы хотели бы применить эту команду (инкремент).

Наверху команда: g/^\s*double precision. Она вывела строки, попавшие под шаблон. Текст при этом не изменился. Я просто вижу все объявления переменых в файле, с номерами строк (set number включен). А мог бы применить замену или что-то еще.
Наверху команда: g/^\s*double precision. Она вывела строки, попавшие под шаблон. Текст при этом не изменился. Я просто вижу все объявления переменых в файле, с номерами строк (set number включен). А мог бы применить замену или что-то еще.

Фильтр проходит по диапазону, если он указан (по умолчанию весь файл), помечая совпадающие строки, а потом применяет к каждой операцию. Поэтому сохранить в файл или сортировать так не получится.

Если на какой-то строке команда не может выполниться, работа не прекращается, обрабатываются прочие строки.

Отмена (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> должен помочь, прервать работу команды.

Все на этом. Удачи!

Путеводитель по каналу