Рано или поздно вы упрётесь в задачу, где одно и то же изменение надо повторить десятки или сотни раз: обернуть каждую строку в кавычки, переставить колонки в CSV, превратить список значений в массив, поправить отступы в логе. Команда-точка . (глава 07) повторяет один последний оператор+движение, но как только правка состоит из нескольких разнородных шагов - . бессильна.
Здесь на сцену выходят макросы. Макрос - это записанная последовательность нажатий клавиш, которую вы проигрываете снова и снова. Принципиально важно понять: вы не "программируете" в каком-то особом языке, вы просто записываете свои обычные нажатия в режиме normal/insert/command-line и затем воспроизводите их. Любое действие, которое вы умеете делать руками, можно записать в макрос. Это делает макросы самым универсальным инструментом автоматизации в Vim - он работает там, где :substitute (глава 11) слишком ограничена, а писать скрипт (глава 22) - избыточно.
Ключевая идея 2026 года, актуальная и для Vim 9.1, и для Neovim 0.12, осталась неизменной с первых версий Vi: макросы хранятся в обычных регистрах (глава 08). А раз так - их можно вставить в текст, отредактировать как строку и заянкать обратно. Это превращает "запись макроса" из одноразового действия в полноценный итеративный процесс отладки.
Основные концепции
Запись: q{reg} ... q
Запись начинается с q, за которой следует имя регистра (буква a-z), и заканчивается повторным нажатием q в режиме normal.
Код: Выделить всё
q a начать запись в регистр a
... ваши действия ...
q остановить запись
Почему регистр, а не отдельное хранилище? Потому что в Vim всё - текст. Регистры хранят и yank-буферы, и макросы, в одном и том же пространстве имён. Из этого вытекает практическое следствие, к которому мы вернёмся: если вы заянкаете что-то в регистр a ("ayy), вы затрёте макрос a. И наоборот - макрос можно вставить в буфер как текст.
Воспроизведение: @{reg}, @@ и {count}@
Код: Выделить всё
Команда | Действие
---------+-------------------------------------------------------
@a | выполнить макрос из регистра a один раз
@@ | повторить последний выполненный @ (тот же регистр)
5@a | выполнить макрос a пять раз подряд
22@a | выполнить макрос a 22 раза
@: | повторить последнюю команду командной строки (:)
@= | выполнить выражение как нажатия (expression register)
@: и @@ - это разные вещи, но они связаны. @: повторяет последнюю команду командной строки (:), а @@ повторяет последний выполненный @. Важная тонкость: после того как вы один раз нажали @:, само это "повторение Ex-команды" становится последним @-действием - и дальше его можно повторять более коротким @@. То есть рабочий цикл для Ex-команды такой же, как для макроса: один раз @:, затем @@, @@, @@ (или 5@: / 5@@, чтобы повторить N раз). Пример: выполнили :s/foo/bar/, перешли на другую строку, @: повторил замену, дальше @@ гоняет её по строкам.
Почему count так важен. Если макрос написан устойчиво (об этом ниже) и спотыкается об ошибку на последней строке, то 100@a просто остановится, когда наткнётся на ошибку (например, j на последней строке файла). Поэтому безопасно задавать count "с запасом" - Vim прервёт выполнение сам.
Макросы хранятся в регистрах - значит, их можно редактировать
Это самая мощная и недооценённая возможность. Записать макрос с первого раза без ошибки получается редко: лишний пробел, неправильное движение, забытый 0 в начале. Вместо того чтобы перезаписывать всё заново, отредактируйте регистр как текст.
Алгоритм:
- Вставьте содержимое регистра в буфер. На пустой строке: :put a (через двоеточие, это Ex-команда :put) либо в normal-режиме "ap.
- Поправьте последовательность нажатий как обычную строку текста.
- Заянкайте всю строку обратно в тот же регистр: установив курсор в начало строки, "ay$ (заянкать от курсора до конца строки в регистр a), либо просто 0"ay$.
Дозапись в конец макроса прописной буквой регистра. Часто после записи макроса понимаешь, что забыл в конце одно-два нажатия (например, финальное j). Перезаписывать всё с нуля не нужно: прописная буква регистра дописывает к существующему содержимому, а не затирает его - это общее свойство регистров (глава 08), и оно работает и для записи макросов, и для yank.Альтернатива для правки команд: командное окно. В Neovim/Vim q: открывает окно истории Ex-команд, q/ - историю поиска. Там можно отредактировать строку как буфер и выполнить её по <CR>. Подробнее о навигации по истории - в главе 10.
- Запись: qA ... q - продолжит запись в регистр a, добавив новые нажатия в конец того, что там уже было. (qa - затирает и пишет заново, qA - дописывает.)
- Правка как текста: "Ay$ дописывает строку в конец регистра a (против "ay$, который перезаписывает).
Рекурсивные макросы: @q внутри q
Обычно макрос проигрывают с count'ом (50@a). Но иногда вы не знаете, сколько элементов обрабатывать - список "до конца файла" или "до первой строки без совпадения". Здесь помогает рекурсивный макрос: внутри записи макрос в конце вызывает сам себя.
Идея: макрос делает одну итерацию, переходит к следующему элементу и в самом конце вызывает @q. Он будет крутиться, пока какое-то движение не выдаст ошибку (например, j на последней строке) - тогда вся рекурсия прервётся.
Критическое условие: регистр должен быть ПУСТ перед записью. Если в регистре q уже что-то лежит, то @q в конце выполнит старое содержимое, и поведение станет непредсказуемым. Поэтому рекурсивный макрос записывают так:
Код: Выделить всё
q q (очистка) начать запись в пустой q и сразу...
q ...остановить - регистр q теперь пуст
q q начать настоящую запись
... действия ...
@ q в конце вызвать сам себя (регистр пока пуст, так что @q ничего не делает при записи)
q остановить запись
На практике рекурсия мощна, но менее предсказуема, чем декларативный диапазон. Современный совет 2026: для "прогнать по строкам" чаще чище и безопаснее :normal и :global (см. ниже) - диапазон там задаётся явно, а не "крутимся до сбоя".
:normal - выполнить нажатия из командной строки
Команда :normal {keys} выполняет переданные нажатия так, будто вы набрали их в normal-режиме. В сочетании с диапазоном это даёт декларативный способ применить макрос к набору строк:
Код: Выделить всё
Команда | Действие
-----------------+-----------------------------------------------------
:normal @a | выполнить макрос a на текущей строке
:%normal @a | выполнить макрос a на каждой строке буфера
:'<,'>normal @a | выполнить макрос a на строках визуального выделения
:10,20normal @a | на строках с 10 по 20
:%normal A; | дописать ; в конец каждой строки
Почему :%normal @a часто лучше рекурсии. Диапазон % (весь файл) или '<,'> (выделение) задаёт ровно те строки, к которым надо применить макрос. Не нужно полагаться на "макрос споткнётся на последней строке". Это читаемо и воспроизводимо.
Осторожно: при ошибке нет атомарного отката. Если макрос внутри :%normal @a (или :g/pat/normal @a) на какой-то строке выдаст ошибку, выполнение прервётся на этой строке - но все строки, обработанные до неё, уже останутся изменёнными. Атомарной транзакции "всё или ничего" здесь нет: частично применённую трансформацию Vim не откатывает сам. К счастью, весь блок :%normal/:g считается одним изменением для undo - то есть один u отменяет разом все правки, сделанные этой командой (а не построчно). Поэтому при сбое: нажмите u, исправьте макрос и запустите команду заново на чистом буфере. Перед опасной массовой правкой полезно сделать :w (или работать на копии), чтобы откат был гарантированным.
:g/pat/normal @a - макрос только по совпадениям
Команда :global (глава 12) выполняет Ex-команду на строках, подходящих под паттерн. В связке с :normal @a вы прогоняете макрос только по тем строкам, где есть совпадение:
Код: Выделить всё
:g/TODO/normal @a выполнить макрос a на каждой строке со словом TODO
:g/^function/normal @a только на строках, начинающихся с function
:v/;$/normal A; на строках БЕЗ точки с запятой в конце - дописать ;
Практические примеры "было -> нажимаем -> стало"
Пример 1. Пронумеровать список и обернуть в кавычки
Дано:
Код: Выделить всё
apple
banana
cherry
Код: Выделить всё
1. "apple",
2. "banana",
3. "cherry",
Код: Выделить всё
q a начать запись в a
I "<Esc> вставить пробел и кавычку в начало (I - в начало строки)
A",<Esc> дописать кавычку и запятую в конец
j перейти на следующую строку (делает макрос устойчивым к count)
q остановить запись
2@a прогнать ещё на двух строках
Пример 2. Поменять местами две колонки CSV
Дано:
Код: Выделить всё
Иванов,Иван
Петров,Пётр
Сидоров,Сидор
Код: Выделить всё
q a
0 в начало строки (всегда стартуем от известной позиции!)
dt, удалить ДО запятой (не включая её) - фамилия в unnamed-регистре
x удалить оставшуюся ведущую запятую
A,<Esc> дописать запятую в конец имени
p вставить вырезанную фамилию после запятой
j на следующую строку
q
Трассировка для строки Иванов,Иван:
Код: Выделить всё
Шаг | Состояние строки | Регистр (unnamed)
---------+---------------------------------+-------------------
0 | Иванов,Иван (курсор на И) | -
dt, | ,Иван (курсор на ,) | Иванов
x | Иван (курсор на И) | Иванов
A,<Esc> | Иван, (курсор на ,) | Иванов
p | Иван,Иванов (вставка после ,) | Иванов
Пример 3. Превратить список в массив (рекурсивный макрос до конца файла)
Дано (число строк заранее неизвестно):
Код: Выделить всё
red
green
blue
yellow
Код: Выделить всё
q q очистить регистр q (запись "ничего")
q
q q настоящая запись
I"<Esc> кавычка в начало
A",<Esc> кавычка и запятая в конец
j следующая строка
@ q вызвать себя; затем...
q остановить запись
@q запустить - крутится до ошибки j на последней строке
Пример 4. Применить макрос только к строкам с паттерном
Дано - лог, где надо пометить [ERROR] строки префиксом >>>:
Код: Выделить всё
[INFO] started
[ERROR] disk full
[INFO] ok
[ERROR] timeout
Код: Выделить всё
qa I>>> <Esc> q записали макрос: дописать ">>> " в начало строки
:g/\[ERROR\]/normal @a применить только к строкам со словом [ERROR]
Лучшие практики 2026
Эти принципы делают макросы надёжными и переносимыми. Они одинаково применимы к Vim 9.1 и Neovim 0.12 - макросы остаются "классической" встроенной механикой, не затронутой переходом на Lua/Vim9script.
- Начинайте макрос с нормализации позиции. Первым нажатием делайте 0 (начало строки), ^ (первый непробельный символ) или gg/G - что уместно. Макрос, который полагается на "где сейчас стоит курсор", сломается при первом же отклонении входных данных. Известная позиция старта = воспроизводимость.
- Делайте макрос устойчивым к содержимому, а не к позиции. Предпочитайте текстовые объекты и поиск (ci", f,, /pattern<CR>) подсчёту символов (3l, 5x). Объект ci( сработает одинаково независимо от длины содержимого скобок, тогда как 3l сломается, если слово другой длины. Это та же логика повторяемости, что и для . (глава 07).
- Завершайте макрос переходом к следующему элементу (j, }, n после поиска). Тогда @@/{count}@ сами движутся по файлу, и тот же макрос годится для рекурсии и для {count}@.
- Для "по всем строкам" предпочитайте :%normal @a, а для отбора - :g/pat/normal @a. Декларативный диапазон надёжнее рекурсии "до ошибки": вы явно видите, какие строки затронуты, и легко ограничиваете выделением '<,'>.
- Удаляйте в blackhole-регистр внутри макросов. Если макрос что-то удаляет, но удалённое вам не нужно для вставки, используйте "_d вместо d - иначе макрос затирает unnamed-регистр и системный буфер обмена (глава 08).
- Очищайте регистр перед записью рекурсивного макроса (qaq, затем qa...q). Непустой регистр - классическая причина "макрос делает что-то странное".
- Не бойтесь редактировать макрос как текст. Быстрее поправить один символ через :put a -> правка -> "ay$, чем перезаписывать длинную последовательность с нуля. Спецклавиши вставляйте через <C-v><Esc> / <C-v><CR>.
- Дописывайте к макросу прописной буквой регистра. Забыли пару нажатий в конце - не переписывайте всё: qA...q дописывает к регистру a (а "Ay$ - при правке текстом). Строчная буква затирает, прописная добавляет.
- Помните про неатомарность массовых правок. При ошибке внутри :%normal/:g уже обработанные строки остаются изменёнными. Весь блок - одно изменение для undo, так что один u откатит его целиком; перед опасной правкой делайте :w.
- Сохраняйте полезные макросы между сессиями. Регистры сохраняются в shada (Neovim) / viminfo (Vim). Чтобы "зафиксировать" макрос на будущее, можно задать его в конфиге: например в Lua для Neovim vim.fn.setreg('a', 'I"\27A",\27j') (где \27 - это <Esc>), или в Vim9script через setreg(). Так макрос станет частью конфигурации (главы 20-22).
- Записали q, забыли регистр. q без последующей буквы в normal-режиме - ничего полезного: нужно сразу qa. Если индикатор recording не появился - запись не началась.
- Не остановили запись. Забыли финальное q - и все последующие нажатия (включая исправления!) пишутся в регистр. Если что-то пошло не так - нажмите q, проверьте регистр через :reg a и перезапишите.
- Затёрли макрос yank'ом. После записи в регистр a любое "ayy перезапишет макрос. Держите макросы в регистрах, которые не используете для yank, или редактируйте осознанно.
- Макрос "привязан" к позиции курсора. Самая частая причина "у меня макрос ломается на третьей строке": не начали с 0/^ или считали символы вместо движений по объектам.
- Рекурсия по непустому регистру. Записали qa...@a q без предварительной очистки - @a внутри выполнит старое содержимое. Всегда qaq перед записью рекурсивного макроса.
- Спецклавиши при правке макроса. Набрали ^ и [ как два символа вместо одного <C-v><Esc> - макрос больше не "нажимает Esc", а вставляет два литерала. Аналогично с ^M (Enter).
- :cdo/:cfdo vs макрос. Для замен по списку quickfix есть свои инструменты (глава 14); макрос через :g/normal хорош для трансформаций внутри одного файла. Не путайте области применения.
- Ожидание, что count "дотянет" сломанный макрос. 100@a остановится на первой ошибке. Это хорошо для устойчивых макросов, но если макрос ошибается уже на второй итерации, count не "перепрыгнет" проблему - чините сам макрос.
- Кавычки и запятые. Возьмите список из 5 слов на отдельных строках. Запишите макрос, который оборачивает строку в двойные кавычки и добавляет запятую в конец, затем переходит на следующую строку. Примените через 3@a, затем повторите через @@.
- Рекурсия до конца файла. Тот же список, но число строк сделайте "неизвестным" (15-20 строк). Запишите рекурсивный макрос (с предварительной очисткой регистра) и прогоните его одним нажатием @q. Убедитесь, что он останавливается на последней строке.
- Только по совпадениям. В файле перемешаны строки [OK] и [FAIL]. Запишите макрос, который дописывает <-- CHECK в конец строки, и примените его только к строкам [FAIL] через :g/\[FAIL\]/normal @a. Затем сделайте обратное через :v/\[FAIL\]/....
- Правка макроса как текста. Запишите намеренно "сломанный" макрос (например, забудьте 0 в начале). Вставьте его в буфер через :put a, найдите ошибку, добавьте 0 в начало строки, заянкайте обратно 0"ay$ и убедитесь, что исправленный макрос работает на всём файле через :%normal @a.
- Колонки. Дан CSV вида имя,значение на 10 строках. Запишите макрос, меняющий колонки местами (значение,имя), начиная курсор с 0 и используя dt,/x/p (помните: dt, не захватывает запятую - иначе получите лишнюю). Проверьте на одной строке, что не возникло двойной запятой, затем примените через :%normal @a.
- Сохранение в конфиг (Neovim/Vim 9). Возьмите рабочий макрос из упражнения 1, посмотрите его содержимое через :reg a, и пропишите его в конфиге через setreg('a', ...) (или Lua vim.fn.setreg), чтобы он был доступен в каждой новой сессии.
- Макрос - это записанная последовательность нажатий: q{reg} начать, q остановить, @{reg} воспроизвести, @@ повторить, {count}@{reg} повторить N раз.
- Макросы живут в обычных регистрах, поэтому их можно вставить в текст (:put a / "ap), отредактировать как строку (спецклавиши - через <C-v><Esc>/<C-v><CR>) и заянкать обратно ("ay$).
- Рекурсивные макросы (@q в конце записи) обрабатывают неизвестное число элементов до первой ошибки; обязательное условие - очистить регистр (qqq) перед записью.
- :normal @a выполняет макрос из командной строки; с диапазоном :%normal @a / :'<,'>normal @a это декларативная альтернатива рекурсии, а :g/pat/normal @a прогоняет макрос только по совпавшим строкам.
- Устойчивый макрос начинается с известной позиции (0/^), оперирует текстовыми объектами и поиском, а не подсчётом символов, и заканчивается переходом к следующему элементу.
- Эта механика неизменна в Vim 9.1 и Neovim 0.12 - макросы остаются самым универсальным встроенным инструментом автоматизации повторяющихся правок.