Eu tenho um arquivo que se parece com este exemplo de brinquedo. Meu arquivo atual possui 4 milhões de linhas, das quais 10 precisam ser excluídas.
ID Data1 Data2
1 100 100
2 100 200
3 200 100
ID Data1 Data2
4 100 100
ID Data1 Data2
5 200 200
Quero excluir as linhas que se parecem com o cabeçalho, exceto a primeira linha.
Arquivo final:
ID Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200
Como posso fazer isso?
text-processing
Gaius Augustus
fonte
fonte
{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
head -1
ficou obsoleto por décadas antes disso.Você pode usar
Isso excluirá as linhas com o ID iniciando na linha 2.
fonte
sed '2,${/^ID Data1 Data2$/d;}' file
(usando o número correto de espaços entre as colunas, é claro)sed
s, não.sed '1!{/ID/d;}'
Para quem não gosta de colchetes
n
significa apass
linha não.1
d
exclua todas as linhas correspondentes que começam com^ID
fonte
sed '1n;/^ID/d'
nome do arquivo. apenas uma sugestãoIDfoo
que não são iguais ao cabeçalho (é improvável que faça diferença nesse caso, mas você nunca sabe).Aqui está uma divertida. Você pode usar
sed
diretamente para retirar todas as cópias da primeira linha e deixar todo o resto no lugar (incluindo a primeira linha).1{h;n;}
coloca a primeira linha no espaço de espera, imprime e lê na próxima linha - pulando o restante dossed
comandos da primeira linha. (Ele também pula o primeiro1
teste para a segunda linha , mas isso não importa, pois esse teste não se aplicaria à segunda linha.)G
anexa uma nova linha seguida pelo conteúdo do espaço de espera no espaço do padrão./^\(.*\)\n\1$/d
exclui o conteúdo do espaço do padrão (pulando para a próxima linha) se a parte após a nova linha (isto é, o que foi acrescentado do espaço de espera) corresponder exatamente à parte antes da nova linha. É aqui que as linhas que duplicam o cabeçalho serão excluídas.s/\n.*$//
exclui a parte do texto que foi adicionada peloG
comando, para que o que é impresso seja apenas a linha de texto do arquivo.No entanto, como a regex é cara, uma abordagem um pouco mais rápida seria usar a mesma condição (negada) e
P
subir para a nova linha se a parte após a nova linha (ou seja, o que foi acrescentado no espaço de espera) não corresponder exatamente à parte antes da nova linha e exclua incondicionalmente o espaço do padrão:Saída quando dada a sua entrada é:
fonte
sed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input
; de alguma forma, é mais fácil para mim ler. :)Aqui estão mais algumas opções que não exigem que você conheça a primeira linha com antecedência:
O
-n
sinalizador diz ao perl para fazer um loop sobre seu arquivo de entrada, salvando cada linha como$_
. O$k=$_ if $.==1;
salva a primeira linha ($.
é o número da linha, portanto$.==1
será válido apenas para a 1ª linha) como$k
. Asprint unless $k eq $_
estampas da linha atual, se não é o mesmo que aquele salvo no$k
.Alternativamente, a mesma coisa em
awk
:Aqui, testamos se a linha atual é igual à que é salva na variável
x
. Se o teste for$0!=x
avaliado como verdadeiro (se a linha atual$0
não for a mesma quex
), a linha será impressa porque a ação padrão para awk em expressões verdadeiras é imprimir. A primeira linha (NR==1
) é salva comox
. Como isso é feito após verificar se a linha atual correspondex
, isso garante que a primeira linha também seja impressa.fonte
!($0 in a)
testes sem criar e evita isso, ou awk pode fazer a mesma lógica que você tem para perl:'$0!=x; NR==1{x=$0}'
ou se a linha de cabeçalho pode estar vazio'NR==1{x=$0;print} $0!=x'
!a[$0]
? Por que isso criaria uma entradaa
?O AWK também é uma ferramenta decente para esse fim. Aqui está um exemplo de execução de código:
Divida :
NR == 1 {print}
nos diz para imprimir a primeira linha do arquivo de textoNR != 1 && $0!~/ID Data1 Data2/
O operador lógico&&
diz ao AWK para imprimir uma linha que não é igual a 1 e não contémID Data1 Data2
. Observe a falta de{print}
parte; em awk, se uma condição de teste for avaliada como verdadeira, é assumido que a linha será impressa.| head -n 10
é apenas uma pequena adição para limitar a saída apenas às 10 primeiras linhas. Não é relevante para aAWK
peça em si, apenas usado para fins de demonstração.Se você desejar isso em um arquivo, redirecione a saída do comando anexando
> newFile.txt
no final do comando, da seguinte maneira:Como ele aguenta? Muito bom, na verdade:
Nota
O arquivo de amostra gerado foi feito para executar um loop de um a um milhão e imprimir as quatro primeiras linhas do seu arquivo (portanto, 4 linhas vezes milhões equivalem a 4 milhões de linhas), o que levou 0,09 segundos.
fonte
ID Data1 Data2 foo
que não são iguais ao cabeçalho (é improvável que faça diferença nesse caso, mas você nunca sabe).Awk, adaptando-se a qualquer cabeçalho automaticamente:
ou seja, na primeira linha, obtenha o cabeçalho e imprima-o e a linha subsequente DIFERENTE desse cabeçalho será impressa.
FNR = Número de registros no arquivo atual, para que você possa ter vários arquivos e fará o mesmo em cada um deles.
fonte
Por uma questão de integridade, a solução Perl IMO um pouco mais elegante do que a @terdon forneceu:
fonte
ID
. Você não tem garantia de que isso não exclua as linhas que devem ser mantidas. Desde que você trouxe elegância, nãog
faz sentido se você usar^
e$
. De fato, todas as suas opçõesm///
são inúteis aqui, excetos
; eles ativam recursos que você não está usando. Assim é o$
,s/^ID.*//s
faria a mesma coisa.Apenas para retroceder um pouco na questão ... parece que talvez sua própria entrada seja o resultado de reunir vários arquivos TSV. Se você puder fazer backup de uma etapa do seu pipeline de processamento (se você é o proprietário ou pode conversar com as pessoas que o fazem), pode usar uma ferramenta com reconhecimento de cabeçalho para concatenar os dados em primeiro lugar e, assim, remover o problema de precisar remova linhas de cabeçalho extras.
Por exemplo, usando Miller :
fonte