Vim: como deletar cada segunda linha?

99

Como fazer no Vim:

ANTES:

aaa
bbb
ccc
ddd
eee
fff

DEPOIS DE:

aaa
ccc
eee
brash
fonte
Você deve ser capaz de usar uma macro: oreillynet.com/mac/blog/2006/07/…
Mark Wilkins
Gosto da riqueza de respostas para esta. Favorecendo.
froggythefrog de

Respostas:

105

Você pode usar uma macro para isso. Faça o seguinte.

  • Comece no modo de comando.
  • Vá para o início do arquivo pressionando gg.
  • Pressione qq.
  • Clique na seta para baixo e pressione dddepois.
  • Pressione q.
  • pressione 10000@q

PS: Para ir para o modo de comando, pressione Escape algumas vezes.

rui
fonte
11
Supondo que seu arquivo tenha menos de 20.000 linhas, é claro :-)
paxdiablo
3
Na verdade você está errado Michael. Isso funciona perfeitamente, pois o VI interromperá a execução da macro assim que atingir o final do arquivo. Você pode experimentá-lo facilmente com o exemplo acima.
rui
1
Bem, como qualquer macro, você precisa acertar na primeira vez, mas depois disso você pode aplicar a qualquer arquivo apenas digitando 10000 @ q. De qualquer forma, acredito que seja apenas uma questão de gosto, então definitivamente não vou iniciar um debate filosófico. :)
rui
3
Eu prefiro usar 'macro'. Usando macro, você 'descreve' como resolveria uma tarefa em particular e deixa o VIM repeti-la para você. RUI, prefiro usar 'j' em vez da tecla de seta.
SolutionYogi
4
:g/^/+ddo usuário stackoverflow.com/users/254635/ib (abaixo) é uma maneira mais elegante de resolver isso.
SergioAraujo
254

Uma forma elegante (e eficiente) de realizar a tarefa é invocar o :+deletecomando removendo a linha seguinte à atual, em cada linha usando o :globalcomando:

:g/^/+d
ib.
fonte
10
Por que essa resposta não obteve mais votos positivos? Isso é tão limpo e simples. Alternativamente, você pode excluir a primeira linha e executar o comando acima para excluir todas as linhas ímpares.
Usagi
3
@Usagi: Obrigado! Eu sinto a mesma coisa sobre isso. E sim, você está absolutamente certo, :1d|g/^/+dexclui as linhas ímpares.
ib.
@Usagi A outra resposta foi selecionada como a 'melhor resposta' 2 anos antes de sua resposta aparecer. Além disso, embora esta seja uma solução mais elegante, ela poderia ter sido escrita de uma maneira mais útil (ou seja, não há menção do que ^ faz, embora a macro seja fácil de entender).
user1271772
3
Desculpe, mas sem uma explicação de como aquele comando executa o comportamento desejado, eu não votaria contra ele e prefiro a resposta aceita, pois entendo como ele atinge o objetivo. É a diferença entre dar um peixe a um homem e ensiná-lo a pescar.
Gabe
3
Não sei, essa resposta me fez sair e aprender a pescar com o comando: g (isto é, me fez google esta página - vim.wikia.com/wiki/Power_of_g ).
snetch de
67

Podemos usar :normalou :normpara executar determinados comandos de modo normal. Fonte.

:%norm jdd
Tadhg
fonte
15
Essa resposta não recebe atenção suficiente, possivelmente por causa da falta de explicação aqui e na folha de dicas vinculada e confusa. o comando norm executa os comandos do modo normal equivalentes às letras que o seguem. Portanto, aqui está executando j para mover uma linha para baixo e dd para excluí-lo. Isso é generalizável para a linha X, adicionando mais 'j's neste caso.
imoatama
5
e% é uma abreviação de 1, $ (execute o seguinte comando da linha 1 até o final)
Lambart
3
Estou interessado em %norm ddjsaber por que não funciona se você deseja excluir linhas estranhas.
Cyker
10
:map ^o ddj^o
^o

Aqui ^ significa CTRL. Macro recursiva para excluir uma linha a cada duas linhas. Escolha bem sua primeira linha e pronto.

Raoul Supercopter
fonte
8

do arquivo de correio vim :

:let i=1 | while i <= line('$') | if (i % 2) | exe i . "delete" | endif | let i += 1 | endwhile

(Para ser digitado em uma linha na linha de comando do vim, excluirá a linha 1,3,5,7, ...)

Fortega
fonte
4
Então eu prefiro !perl -ne 'print unless $. % 2':-)
Alex Brasetvik
E, Alex, eu definitivamente preferiria minha solução à sua, mas vale a pena considerar esta, pois é a única solução sensata usando apenas o próprio vim aqui.
Michael Krelin - hacker de
O modo macro é muito mais fácil.
trusktr de
+1 por causa da flexibilidade com a if (expression)qual também funciona para cada terceira linha etc.
Jens
5

Você sempre pode canalizar um comando shell, o que significa que você pode usar qualquer linguagem de script que desejar:

:%!perl -nle 'print if $. % 2'

(ou use "a menos" em vez de "se", dependendo de quais linhas você deseja)

fino
fonte
Você não precisa -lmudar para Perl ... também, eu diria que awké um pouco mais difundido do que Perl, embora não seja muito mais hoje em dia, e possa fazer isso mais curto:, :%!awk NR\%2ou :%!awk 1-NR\%2para as outras linhas.
efemiente
4
:%!awk -- '++c\%2'

alternativamente

:%!awk -- 'c++\%2'

dependendo de qual metade você deseja eliminar.

Michael Krelin - hacker
fonte
Umm, por que não usar a NRvariável existente ?
efêmero
Certo. Porque eu esqueci disso. ;-) Mas na verdade, a variante "apropriada" será ainda mais longa com o NR. E não haveria duas contrapartes bonitas .
Michael Krelin - hacker de
:%!awk NR\%2ou :%!awk 1-NR\%2, como postei em outro comentário. Isso dificilmente é mais longo.
efêmero
1
1-NRé mais longo que c++. Sim, eu sei que é um personagem. Como eu disse - a verdadeira razão é que me esqueci disso. Enfim, parece que as pessoas não apreciam a beleza do awk aqui.
Michael Krelin - hacker de
E NRé mais curto que ++c:)
efêmero de
3

Você pode usar os recursos de pesquisa e substituição do próprio Vim, como: Coloque o cursor na primeira linha e digite no modo normal:

:.,/fff/s/\n.*\(\n.*\)/\1/g
  • O .,/fff/é o intervalo para a substituição. Significa "desta linha para a linha que corresponde ao regex fff(neste caso, a última linha).
  • s///é o comando substituto. Ele procura um regex e substitui todas as ocorrências dele por uma string. O gno final significa repetir a substituição enquanto a regex continuar sendo encontrada.
  • A regex \n.*\(\n.*\)corresponde a uma nova linha, depois a uma linha inteira ( .*corresponde a qualquer número de caracteres, exceto a uma nova linha), a outra nova linha e a outra linha. Os parênteses \(e \)fazem com que a expressão dentro deles seja gravada, para que possamos usá-la mais tarde usando \1.
  • \1 insere a nova linha agrupada e a linha depois dela de volta, porque não queremos que a próxima linha também vá - queremos apenas que o mecanismo de substituição passe por ela, para não excluí-la na próxima substituição.

Desta forma, você pode controlar o intervalo em que deseja que a exclusão ocorra e não precisa usar nenhuma ferramenta externa.

Daniel Hershcovich
fonte
Ou em $vez de /fff/, para o fim do arquivo, independentemente do conteúdo do arquivo.
efemiente
3

Como outra abordagem, você também pode usar python se seu vim tiver suporte para ele.

:py import vim; cb = vim.current.buffer; b = cb[:]; cb[:] = b[::2]

b = cb[:]copia temporariamente todas as linhas do buffer atual para b. b[::2]obtém cada segunda linha do buffer e as atribui a todo o buffer atual cb[:]. A cópia para bé necessária, pois os objetos de buffer não parecem oferecer suporte à sintaxe de fatia estendida.

Este provavelmente não é o "jeito do vim", mas pode ser mais fácil de lembrar se você conhecer o python.

qzr
fonte
2

Para excluir linhas ímpares (1,3,5, ..) -> :%s/\(.*\)\n\(.*\)\n/\2\r/g

Para excluir linhas pares (2,4,6, ..) -> :%s/\(.*\)\n.*\n/\1\r/g

Pesquise o texto (forma a primeira linha) seguido por um caractere de nova linha e um pouco mais de texto (forma a segunda linha) seguido por outro caractere de nova linha e substitua o acima pela primeira correspondência (linha ímpar) ou segunda correspondência (linha par) seguido por retorno de carro.

kartik kailas
fonte
0

Invoque sed:

:% !sed -e '2~2 d'

^^^^                  pipe file through a shell command
    ^^^^^^            the command is sed, and -e describes an expression as parameter
            ^^^^^     starting with the second line, delete every second line
asdmin
fonte
-1

solução

você pode tentar isso no vim

:1,$-1g/^/+1d

editar

Vou tentar ser mais claro (duas partes)

primeira parte do intervalo de seleção ( :from,to)
segunda parte da ação ( ddeletar)

  • o intervalo é da primeira linha à última, exceto uma linha (:1,$-1 )
  • globaly ( g) delete ( +1d) próxima linha

você pode tentar mover para a primeira linha do seu arquivo e executar :+1d você verá a próxima linha desaparecer

Eu sei que está lamacento, mas é vim e funciona

wdog
fonte
Claro como lama. Obrigado!
Henry Henrinson,