Как поменять порядок строк?

24

Как я могу изменить порядок строк так, чтобы первая строка появлялась в конце, а последняя была первой? (Это могут быть все строки в буфере, диапазон адресов или выбор визуального режима.)

Я хотел бы преобразовать

rat
ox
tiger
⋮
dog
pig

в

pig
dog
⋮
tiger
ox
rat

не прибегая к внешней команде, такой как tac.

200_success
источник
Есть предложения по улучшению тегов по этому вопросу?
200_success
1
может быть, новый «чистый-vi» или похожий тег? Я видел несколько вопросов, которые могли бы получить пользу от тега, который бы указывал на желание не привлекать внешние инструменты. Должен ли я спросить об этом на Meta?
Джон ОМ.
1
@Carpetsmoker (и все, кто заинтересован в том, чтобы следить за этим), вопрос с тегами теперь находится на meta meta.vi.stackexchange.com/questions/1229/…
John O'M.

Ответы:

29

Сила глобальных будет работать здесь:

:g/^/exe "normal ddggP"

Или проще (спасибо @tommcdo)

:g/^/move 0

Первая будет соответствовать каждой строке и каждой строке, удалить ее и вставить в верхнюю часть файла. Когда он перемещается по файлу, он переворачивает текст.

Вторая аналогично соответствует каждой строке и перемещает ее в начало файла.

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

Описание:

gглобальная команда
/^/соответствует любой строке , которая имеет начало (т.е. все строки)
exeвыполнить следующую строку
"normalвыполнить нормальный режим команды
ddудаление строки
ggперейти к верхней части файла
Pпасты выше текущей позиции

move 0 перемещает текущую строку ниже строки 0 (которая помещает ее в позицию 1 или в первую строку файла)

Джон О'М.
источник
6
Вместо :normalкоманды мы можем использовать команду Ex :move 0, которая перемещает строку в начало буфера.
Tommcdo
1
Также :executeнеобходимо, только когда команда должна быть построена динамически, например :execute 'normal' g:user_command.
Tommcdo
@tommcdo хорошие очки! Я имею привычку использовать, :executeпотому что я часто заканчиваю тем, что добавляю другие команды Ex после уже существующей, и мне удобнее уже иметь их :exeтам, чем возвращаться и вставлять их позже. К сожалению, эта привычка просочилась в этот ответ, где он не применяется так часто.
Джон ОМ.
1
Более подробное объяснение моего использования :execute: поскольку оно принимает строку, оно дает четкое определение того, где заканчиваются команды в нормальном режиме, даже если я не создаю строку, мне легче найти сбалансированные кавычки, чем искать <esc>или что угодно, чтобы прекратить режим. Опять же, это личное предпочтение и привычка. :-)
Джон ОМ.
3
Это будет работать для диапазона между прочим: :9,11g/^/move 8... Последнее число должно быть началом диапазона минус 1 (адаптировано из ответа Инго).
Мартин Турной
13

Этот однострочный (для вашего ~/.vimrc) определяет :Reverseкоманду; Вы также можете использовать эту :globalчасть напрямую, но синтаксис :move(который итеративно сдвигает строки до начала диапазона, тем самым обращая его) не легко запомнить:

:command! -bar -range=% Reverse <line1>,<line2>global/^/m<line1>-1
Инго Каркат
источник
1
В качестве FYI для читателей, <line1>& <line2>должны сделать эту работу в диапазоне, то есть: :7,9Reverse(они являются особенностями command, а не globalили move). Более простой :command! -bar -range=% Reverse :global/^/m 0также будет работать, но только для всего буфера ...
Мартин Турной
6

Чистый Вим:

:g/^/m0

Объяснение:

Согласно :help multi-repeat, :gи его двоюродный брат :vработают в два прохода способом.

Первый проход :gпомечает каждую строку, соответствующую {pattern}, в то время как второй проход (очевидно, выполняется начиная с начала файла и до конца) выполняет [cmd]. Вышеупомянутое использование :gиспользует преимущества порядка, в котором обрабатываются строки (что, вероятно, нормально, хотя, вероятно, технически не гарантировано).

Он работает, сначала помечая каждую строку, затем перемещая первую отмеченную строку в начало файла, затем перемещая вторую в верхнюю часть файла (выше строки, перемещенной ранее), затем в третью отмеченную строку (снова выше ранее перемещенной строки). line) и т. д., пока последняя строка в файле не будет перемещена в верхнюю часть, что приведет к реверсированию файла.

Обратите внимание, что если :gобрабатывать строки в любом порядке, кроме сверху вниз, эта команда не будет работать.

Источник: Переверните все линии и Power of g на vim wikia.

Несколько примеров с использованием внешних команд:

  • tac(часть GNU coreutils - catобратная):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail в BSD / OSX (не POSIX-совместимых):

    :%!tail -r
    

    -r Опция -r заставляет ввод отображаться в обратном порядке, построчно.

    Проверьте: man tarдля более подробной информации.

Для большего количества идей, см .:

kenorb
источник
2
Разве это не :g/^/m0то же самое :g/^/move 0, что ответ Джона?
Муру
@muru Я так думаю, но этот короче (согласно vim wikia), и я добавил другое объяснение с несколькими дополнительными примерами использования командных строк.
Кенорб
Да, я проголосовал из-за других команд (я также пришел к посту tac). Но я подозреваю, что отрицательный голос произошел из-за повторения ответа.
Муру
Я знаю, что это tacбыло упомянуто OP, но все другие подобные вопросы в любом случае дублируют это, так что хорошо бы упомянуть это снова. Джон взял этот cmd из комментария @tommcdo, я изначально взял его из DerMike , но я думаю, что он взял его просто из викия, поэтому я дал кредит vim wikia, поэтому он не полностью повторяется, поскольку объяснение совершенно другое.
Кенорб
Это добавляет больше ценности, так как это гораздо более короткая версия с надлежащим объяснением, и я также верю в правильные источники. Использование команд оболочки очень просто и удобно. Если люди не согласны, они могут просто понизить голосование, ничего страшного.
Кенорб
6

В духе функциональности VimL:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))возвращает список всех строк в буфере. '$'это специальный аргумент, для line()которого указывается последняя строка в буфере.
  • reverse(...)переворачивает список ввода, на месте. Можно использовать, reverse(copy(...))если список ввода не должен быть изменен.
  • setline(1, ...)заменяет указанную строку вторым аргументом. Когда второй аргумент является списком, то количество строк, равное длине списка, заменяется содержимым списка.

Если вы хотите, вы также можете определить команду, которая принимает диапазон (по умолчанию %весь буфер)

:command! -bar -range=% Reverse call setline(<line1>, reverse(getline(<line1>, <line2>)))
jamessan
источник
1
Мне нравится этот ответ. Он также не выделяет такие вещи (если они hlsearchвключены), такие как :g/команда из других ответов ... Возможно, производительность еще хуже? Так как он getline(1, line('$'))получает весь буфер в памяти. reverse()кажется, на месте, так что это должно занять очень мало памяти как таковой ...
Мартин Турной
3

Согласно документации Vim usr_12.txt - Умные хитрости

12.4 Обратный порядок строк

:globalКоманда может быть объединена с :moveкомандой , чтобы переместить все строки до первой строки, в результате чего в обратном файл. Команда:

:global/^/m 0

Сокращенное наименование:

:g/^/m 0

^Регулярное выражение соответствует началу строки (даже если строка пуста). Команда :moveперемещает соответствующую строку после мифической нулевой строки, поэтому текущая совпадающая строка становится первой строкой файла. Поскольку :globalкоманда не смущена изменением нумерации строк, она :globalпереходит к соответствию всем оставшимся строкам файла и ставит каждую в качестве первой.

Это также работает на ряде линий. Сначала переместитесь выше первой строки и отметьте ее с помощью mt. Затем переместите курсор на последнюю строку в диапазоне и введите:

:'t+1,.g/^/m 't
jecxjo
источник
1

Используя относительные числа. Абзац начинается со строки 13 и спамит еще 4 строки

 :13,13+4g/^/m12
SergioAraujo
источник