Como excluir de um arquivo de texto todas as linhas que contêm uma sequência específica?


Respostas:


2759

Para remover a linha e imprimir a saída com a saída padrão:

sed '/pattern to match/d' ./infile

Para modificar diretamente o arquivo - não funciona com o BSD sed:

sed -i '/pattern to match/d' ./infile

O mesmo, mas para o BSD sed (Mac OS X e FreeBSD) - não funciona com o GNU sed:

sed -i '' '/pattern to match/d' ./infile

Para modificar diretamente o arquivo (e criar um backup) - funciona com BSD e GNU sed:

sed -i.bak '/pattern to match/d' ./infile

13
Obrigado, mas não parece apagá-lo do arquivo, mas apenas imprima o conteúdo do arquivo de texto sem essa string.
A Clockwork Orange

115
@ Clockwork: sim, você precisa redirecionar a saída para um novo arquivo com algo parecido sed '/pattern to match/d' ./infile > ./newfileou se você quiser fazer uma edição no local, pode adicionar a -ibandeira ao sed como em sed -i '/pattern to match/d' ./infile. Note que o -isinalizador requer GNU sed e não é portátil
SiegeX

16
Para alguns sabores de sed; O sinalizador "-i" do sed exigia uma extensão. (por exemplo sed -i.backup '/pattern to match/d' ./infile) Isso me levou a fazer edições no local.
Avelis

9
@SiegeX Melhor ainda, não aplique comandos como sedos arquivos que não são controlados por versão.
MatrixFrog

84
Mais uma observação para usuários do Mac OS X: por algum motivo, o sinalizador -i exige que um argumento seja passado, mesmo que seja apenas uma sequência vazia, como sed -i '' '/pattern/d' ./infile.
geerlingguy

631

Existem muitas outras maneiras de excluir linhas com sequência específica, além de sed :

AWK

awk '!/pattern/' file > temp && mv temp file

Ruby (1.9+)

ruby -i.bak -ne 'print if not /test/' file

Perl

perl -ni.bak -e "print unless /pattern/" file

Shell (bash 3.2 e posterior)

while read -r line
do
  [[ ! $line =~ pattern ]] && echo "$line"
done <file > o
mv o file

GNU grep

grep -v "pattern" file > temp && mv temp file

E, é claro sed(imprimir o inverso é mais rápido que a exclusão real):

sed -n '/pattern/!p' file

4
como excluir uma linha específica com um padrão e também a linha imediatamente acima dele? Eu tenho uma multa com milhares de tais linhas entre dados diferentes.
Oortcloud_domicile

1
No OS / X, a variação do shell não preserva os espaços iniciais, mas a variação do grep -v funcionou bem para mim.
Paul Beusterien

13
o sedexemplo tem um comportamento diferente, apenas greps! deveria ser algo parecido sed -n -i '/pattern/!p' file.
caesarsol

8
A versão grep não funciona quando todas as linhas correspondem ao padrão. Melhor fazer: grep -v "pattern" file > temp; mv temp fileisso pode se aplicar a alguns dos outros exemplos, dependendo do valor de retorno.
Chris Maes

1
"imprimir o inverso é mais rápido que a exclusão real" - Não está na minha máquina (2012 MacBook Air, OS X 10.13.2). Criar arquivo: seq -f %f 10000000 >foo.txt. sed d: time sed -i '' '/6543210/d' foo.txt0m9.294s reais. sed! p: time sed -i '' -n '/6543210/!p' foo.txt0m13.671s reais. (Para arquivos menores, a diferença é maior.)
jcsahnwaldt disse GoFundMonica

252

Você pode usar o sed para substituir as linhas no lugar de um arquivo. No entanto, parece ser muito mais lento do que usar grep para o inverso em um segundo arquivo e depois mover o segundo arquivo sobre o original.

por exemplo

sed -i '/pattern/d' filename      

ou

grep -v "pattern" filename > filename2; mv filename2 filename

O primeiro comando demora três vezes mais na minha máquina.


19
Votando sua resposta também, apenas porque você tentou uma comparação de desempenho!
anuragw

4
+1 para oferecer a opção de substituir o arquivo atual pela linha grep.
Rhyuk

2
A segunda solução 'grep' também é melhor para arquivos grandes
simoes

3
Estou curioso para saber o que a diferença de desempenho seria se fossesed '/pattern/d' filename > filename2; mv filename2 filename
Pete

9
(usando / usr / share / dict / words do ubuntu) grep e mv: 0.010s | sed no local: 0.197s | sed e mv: 0.031s
ReactiveRaven

77

A maneira mais fácil de fazer isso, com o GNU sed:

sed --in-place '/some string here/d' yourfile

56
Uma dica útil para outras pessoas que se deparam com esse segmento de perguntas e respostas e são novas no script de shell: Opções curtas são boas para usos únicos na linha de comando, mas opções longas devem ser preferidas nos scripts, pois são mais legíveis.
Dennis

3
+1 para o sinalizador - no local. Eu preciso testar isso em arquivos protegidos por permissões. (tem que fazer alguns lavagem usuário.)
Bee Kay

8
Observe que a opção longa está disponível apenas no GNU sed. Usuários de Mac e BSD precisarão instalar o gsed para fazer isso dessa maneira.
18716 Matt

Outra dica: se o seu regex não parece corresponder, tente a -ropção (ou -E, dependendo da sua versão). Isso permite o uso de metacaracteres regex +, ?, {...}e (...).
Rjh 17/09/19

Essa é a resposta correta quando seu disco não tem mais espaço e você não pode copiar o texto para outro arquivo. Este comando faz o que foi questionado?
Ferreirabraga

38

Você pode considerar o uso ex(que é um editor padrão baseado em comandos do Unix):

ex +g/match/d -cwq file

Onde:

  • +executa o comando Ex ( man ex), o mesmo -cque o executa wq(gravar e sair)
  • g/match/d- Comando Ex para excluir linhas com dados match, consulte: Potência de g

O exemplo acima é um método compatível com POSIX para edição in-loco de um arquivo, de acordo com esta publicação, nas especificações exUnix.SE e POSIX .


A diferença sedé que:

sedé um editor ED do S tream , não um editor de arquivos. BashFAQ

A menos que você goste de código não transportável, sobrecarga de E / S e outros efeitos colaterais ruins. Então, basicamente, alguns parâmetros (como no local / -i) são extensões não padrão do FreeBSD e podem não estar disponíveis em outros sistemas operacionais.


5
isso é ótimo ... quando eu faço man exisso me dá o homem para vim, ao que parece exfaz parte do vim ... se eu entendi direito que meios a sintaxe padrão para matché vimregex.com que é semelhante, mas diferente para POSIX e PCRE sabores?
Anentropic

1
:g é um comando compatível com POSIX com algumas pequenas diferenças . Presumo que o PCRE tenha sido baseado nisso.
kenorb

16

Eu estava lutando com isso no Mac. Além disso, eu precisava fazer isso usando substituição de variável.

Então eu usei:

sed -i '' "/$pattern/d" $file

onde $fileé o arquivo onde a exclusão é necessária e $patterné o padrão a ser correspondido para exclusão.

Eu escolhi o ''deste comentário .

O que deve ser observado aqui é o uso de aspas duplas em "/$pattern/d". A variável não funciona quando usamos aspas simples.


3
Mac sedrequer um parâmetro depois -i, então se você não quer um backup, você ainda tem que adicionar uma string vazia:-i ''
wisbucky

Para uso em concha sed -i "/$pattern/d" $file. Obrigado pela sua resposta.
ashwaqar

14

Fiz uma pequena referência com um arquivo que contém aproximadamente 345.000 linhas. O caminho com grepparece ser cerca de 15 vezes mais rápido que o sedmétodo neste caso.

Eu tentei com e sem a configuração LC_ALL = C, não parece alterar significativamente os tempos. A cadeia de pesquisa (CDGA_00004.pdbqt.gz.tar) está em algum lugar no meio do arquivo.

Aqui estão os comandos e os horários:

time sed -i "/CDGA_00004.pdbqt.gz.tar/d" /tmp/input.txt

real    0m0.711s
user    0m0.179s
sys     0m0.530s

time perl -ni -e 'print unless /CDGA_00004.pdbqt.gz.tar/' /tmp/input.txt

real    0m0.105s
user    0m0.088s
sys     0m0.016s

time (grep -v CDGA_00004.pdbqt.gz.tar /tmp/input.txt > /tmp/input.tmp; mv /tmp/input.tmp /tmp/input.txt )

real    0m0.046s
user    0m0.014s
sys     0m0.019s

Em que plataforma estás? Quais versões do sed / perl / grep você usa?
hagello 21/02

A plataforma que eu uso é o Linux (Gentoo). A versão sed é GNU sed v 4.2.2, a versão perl perl 5 (não posso dizer qual revisão eu usei no momento do teste) e grep (GNU) é a versão 3.0.
Jadzia

14

Você também pode usar isso:

 grep -v 'pattern' filename

Aqui -vserá impresso apenas outro que não o seu padrão (isso significa correspondência invertida).


Como posso excluir linhas em um diretório que contêm uma seqüência específica
namannimmo

13

Para obter um resultado semelhante no local, grepvocê pode fazer o seguinte:

echo "$(grep -v "pattern" filename)" >filename

4
Isso é bom apenas para o bashshell ou similar (não tcsh).
esmit


4
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

O primeiro comando edita o (s) arquivo (s) no local (-i).

O segundo comando faz a mesma coisa, mas mantém uma cópia ou backup dos arquivos originais, adicionando .bk aos nomes dos arquivos (.bk pode ser alterado para qualquer coisa).


2

echo -e "/thing_to_delete\ndd\033:x\n" | vim file_to_edit.txt


2

Caso alguém queira fazer isso para correspondências exatas de strings, você pode usar a -wflag em grep - w para todo. Ou seja, por exemplo, se você deseja excluir as linhas que têm o número 11, mas mantenha as linhas com o número 111:

-bash-4.1$ head file
1
11
111

-bash-4.1$ grep -v "11" file
1

-bash-4.1$ grep -w -v "11" file
1
111

Também funciona com o -fsinalizador se você deseja excluir vários padrões exatos ao mesmo tempo. Se "lista negra" for um arquivo com vários padrões em cada linha que você deseja excluir de "arquivo":

grep -w -v -f blacklist file

Um pouco enganador. -w, --word-regexp Select only those lines containing matches that form whole words.vs-x, --line-regexp Select only those matches that exactly match the whole line. For a regular expression pattern, this is like parenthesizing the pattern and then surrounding it with ^ and $.
Sai


0

para mostrar o texto tratado no console

cat filename | sed '/text to remove/d' 

salvar texto tratado em um arquivo

cat filename | sed '/text to remove/d' > newfile

anexar informações de texto tratadas a um arquivo existente

cat filename | sed '/text to remove/d' >> newfile

para tratar o texto já tratado, neste caso, remova mais linhas do que foi removido

cat filename | sed '/text to remove/d' | sed '/remove this too/d' | more

o | moretexto será exibido em pedaços de uma página por vez.


0

Você pode usar o bom e velho edpara editar um arquivo de maneira semelhante à resposta usada ex. A grande diferença nesse caso é que edrecebe seus comandos por meio da entrada padrão, não como argumentos de linha de comando como expode. Ao usá-lo em um script, a maneira usual de acomodar isso é usar printfpara canalizar comandos para ele:

printf "%s\n" "g/pattern/d" w | ed -s filename

ou com um heredoc:

ed -s filename <<EOF
g/pattern/d
w
EOF
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.