Как поменять каждые 4 строки?

13

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

У меня вопрос: я знаю, как перевернуть ВСЕ строки в файле (: g / ^ / m0 среди других способов), но есть ли способ отменить каждые 4 строки в файле, чтобы

line1
line2
line3
line4
line5
line6
line7
line8
...

становится

line4
line3
line2
line1
line8
line7
line6
line5
...

Вы можете предположить, что в таких файлах всегда будет точное кратное 4 строкам.

ablewasiereisawelba
источник
2
О вашем "ps": если вы вставляете 4 пробела перед строкой, это интерпретируется как код (вы можете выбрать текст и использовать кнопки форматирования вверху). Вы можете найти более подробную информацию о значке допроса в верхней части .
mMontu

Ответы:

15

Для этого можно использовать команду, :Reverseописанную в Vim Wiki (вы можете включить ее в свою, .vimrcчтобы сделать ее постоянной):

command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl

Затем вы можете записать макрос для запуска команды через каждые четыре строки:

qmV3j:Reverse<cr>4jq
1000@m

Объяснение:

  • qm: «q» в обычном режиме запускает (и останавливает) запись макроса в заданном регистре (регистр «m», в данном случае, но это может быть любая другая буква)
  • V: войдите в визуальный режим и выберите текущую строку
  • 3j: расширить визуальный выбор до следующих 3 строк
  • :Reverse<cr>: выполнить команду Reverse на выбранных строках ( <cr>здесь обозначает enterключ)
  • 4j: перейти к следующим неизменным строкам
  • q: останавливает запись макроса
  • 1000@m: запустить макрос, записанный в регистре 'm' 1000 раз (вы можете увеличить это число, если ваш файл больше 4000 строк)

Редактировать:

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

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: если регистр, указанный для qзаглавных букв, макрос добавляется в регистр (что также полезно, когда вы понимаете, когда пропустили последние шаги в сложном макросе)
  • @m: запустить макрос в реестре m
  • q: перестать добавлять макрос

Несмотря на то, что он создан как макрос, вы можете создать команду для этого, если это обычная задача в вашем рабочем процессе:

function! Reverse4()
   let reg_m = @m
   let @m = '<c-r><c-r>m'
   normal! @m
   let @m = reg_m
endfunction
command! Reverse4 call Reverse4()
  • function! Reverse4()/ endfunction: определяет новую функцию
  • let reg_m = @m: сохраняет текущее содержимое реестра m
  • let @m = "<c-r><c-r>m": вставьте макрос в регистр m- обратите внимание, что <c-r>это обозначение vim для Ctrl+ rи что вы должны ввести это, а не копировать / вставить, чтобы ваша строка была похожа let @m = 'V3j:Reverse^M4j@m'и содержала специальный символ (^ M)
  • normal! @m: нормальная команда запускает свой аргумент, как это было набрано в обычном режиме, поэтому она запускает рекурсивный макрос
  • let @m = reg_m: восстанавливает содержимое регистра m, поэтому вам не нужно помнить, что этот регистр используется в этой функции, и избегать его использования

  • command! Reverse4 call Reverse4(): создать новую команду для этой функции

В зависимости от ваших потребностей вы можете улучшить его, например: передать аргумент команде и функции, чтобы он работал для любого количества строк, а не фиксировался в группах по 4 строки.

mMontu
источник
Вместо 1000@qхака, вы можете также использовать рекурсивный макрос: qmqqm ... @mq@m.
дверная ручка
Когда я попытался запустить «qmV3j: Reverse <cr> 4jq», он сказал «E492: не команда редактора». Я должен делать что-то не так. Кстати, я использую - и когда-либо использовал - GVIM. Я должен был упомянуть это в первую очередь.
ablewasiereisawelba
Ах, неважно. Я понял это - я не должен печатать ":" перед частью "qmV3j: Reverse <cr> 4jq". Это сработало! Большое спасибо, mMontu. Если я могу спросить - как мне включить команду ": Reverse" в мой .vimrc?
ablewasiereisawelba
3
@ablewasiereisawelba Чтобы сделать команду доступной постоянно, просто напишите строку command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohlгде-нибудь в вашем vimrc.
сагино
@saginaw О, я не поняла, что это так просто. Спасибо.
ablewasiereisawelba
9

Как и во всех повторяющихся действиях , если вы можете сделать что-то один раз с основными операциями редактирования, то вы можете легко сделать это много раз, записав макрос.

(В этом случае я буду использовать рекурсивный макрос, но вы можете просто записать нерекурсивный макрос и воспроизвести его несколько раз, нажимая @@или используя счетчик.)

ggqqqqqddjjpkddkkPjddpjj@qq@q

Сломан

  1. gg: Перейти к началу файла.
  2. qqq: Очистить регистр q. Мы увидим, почему это необходимо на шаге 5.
  3. qq: Начать запись макроса для регистрации q
  4. ddjjpkddkkPjddpjj: Серия операций, которая переупорядочивает первые четыре строки, просто удаляя и вставляя их вручную. (Это может показаться сложным на первый взгляд, но не обманывайтесь Это очень простой вещи , которые вы бы узнали в первые 5 минут или так. vimtutor: j, k, dd, p, P)
  5. @q: Вызовите макрос! На этом этапе регистр qничего не содержит (потому что мы очистили его на шаге 2), поэтому ничего не произойдет.
  6. q: Остановить запись макроса.
  7. @qВоспроизведите рекурсивный макрос столько раз, сколько необходимо.

NB Выше не даст желаемых результатов, если файл содержит количество строк, которые не делятся точно на четыре. Чтобы досрочно остановить макрос, если не осталось четырех строк, нам нужно выполнить команду нормального режима Vim, которая потерпит неудачу, прежде чем мы начнем применять изменения в шаге 4. Мы можем сделать это, пытаясь двигаться вниз линию три раза (и затем обратно), прежде чем мы начнем перемещать линии:jjj3k

Таким образом:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q
Богатый
источник
Почему вы явно очищаете регистр q? Если вы просто запишите это, все, что там было, все равно будет перезаписано ...
Wildcard
4
@Wildcard Поскольку это рекурсивный макрос, при первом вызове содержимого регистра qон должен быть пустым, иначе он испортит ваши издания.
сагино
Очень хорошо. Я попробовал это в интерактивном режиме, не пытаясь набрать точную последовательность вашей команды и завелся с помощью:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Wildcard
1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this- обратите внимание, что вам не нужно пересоздавать макрос каждый раз, когда вам нужно его использовать, так как его можно сохранить как функцию / команду в вашем vimrc.
mMontu
1
@ablewasiereisawelba Я рад, что вы нашли "Edit" полезным! Поскольку это был ваш первый пост, возможно, вы не знаете, как работает уведомление StackExchange: когда вы публикуете комментарий, автор ответа / вопроса получает уведомление; другие лица получат уведомление, только если вы добавите @ <ник>. Таким образом, только Рич получил уведомления о ваших последних сообщениях.
mMontu
7

Вы также можете сделать это с помощью Exкоманды, использующей sedв качестве внешнего фильтра:

:%!sed -n 'h;n;G;h;n;G;h;n;G;p'

Эта версия будет игнорировать (удалять) любые дополнительные строки, превышающие число, кратное 4. Чтобы сохранить последний набор из менее чем 4 строк (обратный), используйте:

:%!sed -n '$p;h;n;G;$p;h;n;G;$p;h;n;G;p'

%Здесь означает «Каждую строку в буфере.»

Команда !означает «Выполнить следующую команду с указанными строками в качестве ввода и заменить указанные строки выводом команды». (Это называется фильтром; очень удобно для таких вещей, как сортировка, например, :%!sortсортировка всех строк в вашем файле; :2,8!sortсортировка строк 2-8 и т. Д.)

sedявляется редактором потоков и доступен во всех Unix-подобных системах. Ключевыми понятиями, sedиспользуемыми здесь, являются «пространство образца» (которое по умолчанию просто содержит каждую строку ввода по очереди) и «пространство удержания» (где вы можете вставить дополнительный текст при использовании, sedчтобы сохранить его при обработке других строки ввода).

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

$pв sedкоманде означает «Если вы находитесь на последней строке sedввода, напечатайте (пробел)».

h означает «вставить текущее содержимое« пространства образца »в« пространство удержания », перезаписывая все, что там есть».

n означает «заменить содержимое« пространства образца »следующей строкой из ввода».

G означает «добавить к« пробелу »: символ новой строки, за которым следует содержание« пробела »».

Взятые вместе, sedкоманда запоминает четыре строки вывода, обращая их вспять по мере их сохранения, а затем печатает их. Эти $pкоманды , добавленные во второй версии убедиться , что если последняя строка файла достигается за исключением на кратному 4 линии, линии по - прежнему печатаются.


Для альтернативного, интерактивного подхода, все еще без использования специфических для Vim функций, а также без использования внешнего фильтра:

:4

перейти на четвертую линию.

:.m -4 | +3m . | +2m . | +5

чтобы повернуть предыдущие четыре строки (1-4) и оставить курсор на строке 8.

.m -4перемещает текущую строку сразу после строки на четыре строки назад (оставляя курсор на перемещенной строке).

+3m .перемещает строку, которая находится на 3 строки после текущей строки, сразу после текущей строки, оставляя курсор на перемещенной строке. +2m .конечно работает так же.

+5 помещает курсор на пять строк вниз от того места, где он находится.

Повторите по желанию.

В Vim вы можете повторить всю эту команду с помощью @:, а затем повторите с @@. В POSIX viили exвам нужно будет вставить :.m -4 | +3m . | +2m . | +5 в виде строки текста, удалить его в именованный буфер (регистр), а затем выполнить этот именованный буфер (регистр).

Таким образом, в exрежиме, реверсирование строк в интерактивном режиме с использованием только указанных в POSIX функций и начало с 17 строк текста:

Entering Ex mode.  Type "visual" to go to Normal mode.
:0a     # Append following text after "line 0" (i.e. insert at start of file).
.m -4 | +3m . | +2m . | +5
.       # End text insertion
:d k    # Delete that line to register k
line1   # This is a printout of the current line
:4      # Move to line 4
line4
:@k     # Execute register k to reverse lines 1-4
line8
:@@     # Execute register k again
line12
:@@     # Execute register k again
line16
:@@     # Execute register k again
line17
:%p     # Print the whole buffer (just to see what was done)
line4
line3
line2
line1
line8
line7
line6
line5
line12
line11
line10
line9
line16
line15
line14
line13
line17
:wq     # Save and quit

Дальнейшее чтение:

Wildcard
источник
Не могли бы вы объяснить немного больше вашего решения?
vappolinario
3
@vappolinario, я добавил некоторые дополнительные объяснения. Это помогает? :)
Wildcard
Вау, очень длинный ответ. :) Вообще-то, у меня есть sed, но я просто использовал его с одним простым скриптом, который я где-то нашел - возможно, на этой доске - это было решением проблемы, с которой я столкнулся. Тем не менее, когда я пытался запустить предложенную строку, он говорит: «sed» не распознается как внутренняя или внешняя команда, работающая программа или пакетный файл ». Я не уверен, нужно ли мне иметь sed в папке vim или как.
ablewasiereisawelba
1
@ablewasiereisawelba, если вы работаете на компьютере с Windows и не используете Cygwin (или MobaXterm или аналогичный), вы можете попробовать другой метод, который я дал: повторять до тех 4G:.m -4 | +3m . | +2m . | +5<Enter>@:пор, @@пока ваш файл не будет полностью обработан. Я рекомендую установить MobaXterm, хотя. :)
Wildcard
@Wildcard О, хорошо, сейчас я проверяю MobaXterm. :)
ablewasiereisawelba
7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Простой английский: для каждой строки перемещайте текущую строку вверх lnum % 4, если только lnum % 4 == 0в этом случае не перемещайте текущую строку вверх 4.

Кроме того, чтобы инвертировать все nстроки, замените 4's' в приведенной выше команде на n.

djjcast
источник
2
Очень хорошая однострочная команда. Спасибо, djjcast.
ablewasiereisawelba