Eu estava olhando para essa pergunta e depois me perguntei como poderia implementar minha resposta que usa
sed
usando puramente
POSIX
ex
.
O truque é que, enquanto sed
eu posso comparar o espaço de espera com o espaço do padrão para ver se eles são exatamente equivalentes (com
G;/^\(.*\)\n\1$/{do something}
), não conheço nenhuma maneira de fazer esse teste ex
.
Eu sei que no Vim eu poderia Y
criar a primeira linha e digitar
:2,$g/<C-r>0/d
para quase fazer o que estou especificando - mas se a primeira linha contiver algo além de um texto alfanumérico muito simples, isso se tornará arriscado, pois a linha está sendo inserida como uma
expressão regular , não apenas uma sequência para comparação. (E se a primeira linha contiver uma barra, o restante da linha será interpretado como um comando!)
Portanto, se eu quiser excluir todas as linhas myfile
idênticas à primeira linha - mas não excluir a primeira linha - como eu poderia fazer isso usando ex
? Por falar nisso, como eu poderia fazer isso usando vi
?
Existe uma maneira POSIX de excluir uma linha se ela corresponder exatamente a outra linha?
Talvez algo como esta sintaxe imaginária:
:2,$g/**lines equal to "0**/d
fonte
:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
sed
como um filtro de dentroex
, e executar toda a minhased
resposta sobre todo o tampão ... que iria trabalhar, é claro (e é realmente portátil ao contráriosed -i
).<C-r>0
muito boa. Não tenho certeza de que você poderia fazer melhor apenas com os comandos Ex, porque você precisa proteger caracteres especiais. Sem a restrição compatível com POSIX, acho que você usaria a opção muito nomagic\V
e protegeria a barra invertida (porque mantém seu significado especial mesmo\V
) com aescape()
função cujo segundo argumento é uma string que contém todos os caracteres dos quais você deseja escapar / proteger .:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'
Ou você poderia usar outro caractere para o delimitador de padrão como um ponto e vírgula. Nesse caso, você não precisaria proteger uma barra no padrão. Isso daria algo como::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
sed
também muito boa. Com o Vim, você frequentemente delega certas tarefas especiais para outros programas esed
é provavelmente um bom exemplo disso. A propósito, você não precisa executarsed
todo o seu buffer. Se você deseja executá-lo apenas em uma parte do buffer, pode dar um intervalo. Por exemplo, se você deseja filtrar apenas as linhas entre 50 e 100, você pode digitar::50,100!<your sed command>
.Respostas:
Vim
No Vim, você pode combinar qualquer caractere, incluindo nova linha, com
\_.
. Você pode usar isso para construir um padrão que corresponda a uma linha inteira, qualquer quantidade de material e, em seguida, a mesma linha:Agora você deseja excluir todas as linhas em um arquivo que correspondem à primeira, sem incluir a primeira. A substituição para excluir a última linha que corresponde à primeira é:
Você pode usar
:global
para se certificar de que a substituição seja repetida várias vezes para excluir todas as linhas:POSIX ex
@saginaw mostra uma maneira mais clara de fazer isso no Vim em um comentário à sua pergunta, mas podemos adaptar a técnica acima para o POSIX ex.
Para fazer isso de uma maneira compatível com POSIX, é necessário desabilitar a correspondência de várias linhas, mas você ainda pode fazer uso de referências anteriores. Isso requer algum trabalho extra:
Aqui está o detalhamento:
Portanto, se a linha atual for uma duplicata da linha 1, o registro x conterá
d
. A execução excluirá a linha atual. Se não for uma duplicata, ela conterá um absurdo prefixado com o"
qual, quando executado, não funcionará, pois"
inicia um comentário. Não sei se essa é a maneira mais legal de fazer isso, é apenas a primeira que me veio à mente!Acontece que a primeira linha não pode ser excluída porque o processo de cópia altera temporariamente o que é a linha 1. Se não fosse esse o caso, você poderia prefixar o
:g
com um2,$
intervalo.Testado no Vim e ex-vi versão 4.0.
EDITAR
E uma maneira mais simples, que escapa caracteres especiais para criar um padrão de pesquisa (com
'nomagic'
conjunto), cria um:global
comando e o executa:Você não pode fazer isso como uma linha, pois você teria um aninhado
:global
, o que não é permitido.fonte
Parece que a única maneira do POSIX de fazer isso é usar um filtro externo, como
sed
.Por exemplo, para excluir a 17ª linha do seu arquivo apenas se for exatamente idêntica à 5ª linha e, de outro modo, deixá-lo inalterado, você pode fazer o seguinte:
(Você pode executar
sed
o buffer inteiro aqui, ou somente nas linhas 5-17, mas no primeiro caso, você está fazendo uma filtragem desnecessária - não é grande coisa - e, no último caso, teria que usar o números 1 e 13 em seused
comando, em vez de 5 e 17. Confuso.)Como
sed
apenas uma única passagem para frente, não há maneira fácil de fazer o inverso e excluir a 5ª linha apenas se for idêntica à 17ª linha. Eu tentei por um tempo como um ponto de curiosidade ... é complicado .Inovação - Você pode fazer o seguinte:
Este é realmente o método mais geral. Da mesma forma, pode ser usado para fornecer o mesmo resultado que o primeiro comando (e excluir a 17ª linha apenas se for idêntica à 5ª linha) da seguinte forma:
Para usos mais amplos, como excluir todas as linhas do arquivo idênticas à linha 37 e deixar intacta a linha 37, você pode fazer o seguinte:
A conclusão aqui é que, para verificar se duas linhas são idênticas, a melhor ferramenta é
sed
nãoex
. Mas como o DevSolar mencionou em um comentário , isso não é um fracassovi
ouex
- eles foram projetados para funcionar com as ferramentas do Unix; essa é uma grande força.fonte