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

1789

Como eu usaria o sed para excluir todas as linhas em um arquivo de texto que contenham uma sequência específica?

Laranja mecânica
fonte

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
SiegeX
fonte
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
Kurumi
fonte
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.

slashdottir
fonte
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
Kevin Nguyen
fonte
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.

kenorb
fonte
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.

Aniket Sinha
fonte
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
Jadzia
fonte
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).

Bhuvanesh
fonte
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
Jahid
fonte
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).

Kjetil S.
fonte
2

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

Shizzmo
fonte
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
FatihSarigol
fonte
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
1
cat filename | grep -v "pattern" > filename.1
mv filename.1 filename
Andrey Izman
fonte
Você está substituindo um arquivo enquanto ele ainda está em uso.
Davor Cubranic
@DavorCubranic fixado
Andrey Izman
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.

nassim
fonte
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
Shawn
fonte