Удалить все, кроме регулярного выражения в Vim

Моим конкретным случаем является текстовый документ, который содержит много текстовых и IPv4-адресов. Я хочу удалить все, кроме IP-адресов.

Я могу использовать :vglobal для поиска ([0-9]{1,3}\.){3}[0-9]{1,3} и удалить все строки без IP-адресов, но после этого я знаю только, как искать всю строку и выбирать соответствующий текст. Есть ли более простой способ.

Короче говоря, я ищу способ сделать следующее без использования внешней программы (например, grep):

grep --extended-regexp --only-matching --regexp="([0-9]{1,3}\.){3}[0-9]{1,3}"

Вызов grep из vim может потребовать адаптации моего регулярного выражения (например: удаление \v). Использование инкрементного поиска vim показывает мне, что у меня есть правильный шаблон, и я не хочу проверять свое регулярное выражение в grep тоже.


Изменить: Благодаря Петру, здесь функция, которую я сейчас использую. (C - это регистр, который я обычно clobber в моих функциях.)

"" Remove all text except what matches the current search result
"" The opposite of :%s///g (which clears all instances of the current search).
function! ClearAllButMatches()
    let old = @c
    let @c=""
    %s//\=setreg('C', submatch(0), 'l')/g
    %d _
    put c
    0d _
    let @c = old
endfunction

Edit2: Я сделал команду, которая принимает диапазоны (но по умолчанию - весь файл).

"" Remove all text except what matches the current search result. Will put each
"" match on its own line. This is the opposite of :%s///g (which clears all
"" instances of the current search).
function! s:ClearAllButMatches() range
    let is_whole_file = a:firstline == 1 && a:lastline == line('$')

    let old_c = @c

    let @c=""
    exec a:firstline .','. a:lastline .'sub//\=setreg("C", submatch(0), "l")/g'
    exec a:firstline .','. a:lastline .'delete _'
    put! c

    "" I actually want the above to replace the whole selection with c, but I'll
    "" settle for removing the blank line that left when deleting the file
    "" contents.
    if is_whole_file
        $delete _
    endif

    let @c = old_c
endfunction
command! -range=% ClearAllButMatches <line1>,<line2>call s:ClearAllButMatches()

Ответы

Ответ 1

Этот эффект может быть достигнут с помощью замены подзамены-специальной замены и setreg() linewise

:let @a=""
:%s//\=setreg('A', submatch(0), 'l')/g
:%d _
:pu a
:0d _

или все в одной строке как таковой:

:let @a=""|%s//\=setreg('A', submatch(0), 'l')/g|%d _|pu a|0d _

Обзор. Используя подстановку для добавления каждого совпадения в регистр "a", затем замените весь буфер содержимым регистра "a"

Пояснение:

  • let @a="" запустите регистр "a", который мы добавим в
  • %s//\=setreg('A', submatch(0), 'l')/g заменить глобально, используя последний шаблон
  • \=expr заменит шаблон содержимым выражения
  • submatch(0) получить всю строку того, что только что сопоставлено
  • setreg('A', submatch(0), 'l') append (примечание: капитал "a" ) к @a совпадающей строке, но linewise
  • %d _ удалите каждую строку в регистр черных дыр (aka @_)
  • pu a помещает содержимое @a в буфер
  • 0d _ удалить первую строку

Заботы:

  • Это приведет к удалению одного из ваших регистров. В этом примере trashed @a
  • Использует последний шаблон поиска. Хотя вы можете изменить команду substitute любым желаемым шаблоном: %s/<pattern>/\=setreg('A', submatch(0), 'l')/g

Для получения дополнительной справки

:h :s\=
:h :[email protected]
:h submatch()
:h setreg()
:h :d
:h :p

Ответ 2

Предполагая, что <ip> является вашим регулярным выражением в соответствии с IP-адресом, я полагаю, вы могли бы сделать что-то вроде:

:%s/.\{-}\(<ip>\).*/\1/g

где \1 - первая совпадающая группа (только адрес) и .\{-} используется для не-жадного соответствия.

Ответ 3

:set nowrapscan
:let @a=""
gg0qac/\v(\d{1,3}\.){3}\d{1,3}<CR><CR><Esc>//e+1<CR>@[email protected]

Пояснение:

  • set nowrapscan отключает возможность поиска "мимо конца файла".
  • let @a="": пустой регистр.
  • gg0: перейдите к первому столбцу (0) первой строки (gg).
  • qa: начните писать макросы.
  • c/{pattern}<CR>: изменить до шаблона.
  • c{motion}<CR><ESC>: замените текст на новую строку (здесь {motion} есть /{pat}<CR>).
  • //e+1<CR>: найдите последний шаблон, перейдите на один символ слева от его конца (обертывает новую строку, но если ваши строки выглядят так: IP<newline>IP, могут быть проблемы).
  • @a: выполнить @a макросы (при его записи он пуст, но когда вы закончите, он повторит шаги с 1 по 7, пока не получится ошибка).
  • q: конец записи @a.
  • @a: выполнить макросы @a.
  • dG: удалить до конца файла.

Ответ 4

Короче говоря, я ищу способ сделать это, не выходя из vim

достаточно просто:

:1,$! grep --extended-regexp --only-matching --regexp="([0-9]{1,3}\.){3}[0-9]{1,3}"

(хотя я фактически проголосовал за ответ на замену icecrime)