seené uma matriz associativa para a qual o Awk passará todas as linhas do arquivo. Se uma linha não estiver na matriz, ela seen[$0]será avaliada como falsa. O !operador NOT é lógico e inverte o falso para verdadeiro. O Awk imprimirá as linhas em que a expressão é avaliada como verdadeira. Os ++incrementos seenpara que, seen[$0] == 1após a primeira vez que uma linha seja encontrada seen[$0] == 2, e assim por diante.
O Awk avalia tudo menos 0e ""(string vazia) como true. Se uma linha duplicada for inserida, seenela !seen[$0]será avaliada como falsa e a linha não será gravada na saída.
Para salvá-lo em um arquivo que pode fazer issoawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal
5
Uma ressalva importante aqui: se você precisar fazer isso para vários arquivos, e juntar mais arquivos no final do comando, ou usar um curinga ... a matriz 'vista' será preenchida com linhas duplicadas de TODOS os arquivos. Se você em vez disso quer tratar cada arquivo de forma independente, você precisa fazer algo comofor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9
@ NickK9, que a duplicação cumulativa de vários arquivos é impressionante por si só. Dica agradável
# delete duplicate, consecutive lines from a file (emulates "uniq").# First line in a set of duplicate lines is kept, rest are deleted.
sed '$!N; /^\(.*\)\n\1$/!P; D'# delete duplicate, nonconsecutive lines from a file. Beware not to# overflow the buffer size of the hold space, or else use GNU sed.
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
geekery ;-) +1, mas o consumo de recursos é inevitável.
Michael Krelin - hacker 18/09/09
3
'$! N; /^(.*)\n\1$/!P; D 'significa "Se você não estiver na última linha, leia em outra linha. Agora, veja o que você tem e, se não for seguido por uma nova linha e, em seguida, o mesmo material novamente, imprima o material. Agora exclua o material (até a nova linha). "
Beta
2
'G; s / \ n / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ n //; h; P 'significa, grosso modo, "Anexar todo o espaço de espera nesta linha; se você ver uma linha duplicada jogar tudo fora, copie toda a bagunça de volta para o espaço de espera e imprima a primeira parte (que é a linha que você acabou de leia. "
Beta
A $!peça é necessária? Não sed 'N; /^\(.*\)\n\1$/!P; D'faz a mesma coisa? Não consigo criar um exemplo em que os dois sejam diferentes na minha máquina (fwiw, tentei uma linha vazia no final com as duas versões e ambas estavam bem).
28612 eddi
1
Quase 7 anos depois e ninguém respondeu @amichair ... <sniff> me deixa triste. ;) Enfim, [ -~]representa um intervalo de caracteres ASCII de 0x20 (espaço) a 0x7E (til). Esses são considerados os caracteres ASCII imprimíveis (a página vinculada também possui 0x7F / delete, mas isso não parece correto). Isso faz com que a solução seja quebrada para quem não usa ASCII ou para quem digita, por exemplo, caracteres de tabulação. O mais portátil [^\n]inclui muito mais caracteres ... todos, exceto um, de fato.
Camada B,
14
One-liner Perl semelhante à solução awk de @ jonas:
perl -ne 'print if ! $x{$_}++' file
Essa variação remove o espaço em branco à direita antes de comparar:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Essa variação edita o arquivo no local:
perl -i -ne 'print if ! $x{$_}++' file
Essa variação edita o arquivo no local e faz um backup file.bak
A linha única que Andre Miller postou acima funciona, exceto nas versões recentes do sed quando o arquivo de entrada termina com uma linha em branco e sem caracteres. No meu Mac, minha CPU gira.
Loop infinito se a última linha estiver em branco e não tiver caracteres :
O mantenedor do GNU sed considerou que, apesar dos problemas de portabilidade que
isso causaria, alterar o comando N para imprimir (em vez de
excluir) o espaço do padrão era mais consistente com as intuições de alguém
sobre como deveria se comportar um comando para "acrescentar a próxima linha" .
Outro fato favorável à mudança foi que "{N; command;}"
excluirá a última linha se o arquivo tiver um número ímpar de linhas, mas
imprimirá a última linha se o arquivo tiver um número par de linhas.
Para converter scripts que usavam o antigo comportamento de N (excluindo
o espaço do padrão ao atingir o EOF) em scripts compatíveis com
todas as versões do sed, altere um "N" isolado; para "$ d; N;" .
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
Explica:
$!N;: se a linha atual NÃO for a última, use N comando para ler a próxima linha pattern space.
/^(.*)\n\1$/!P: se o conteúdo da corrente pattern spaceé dois duplicate stringseparados por \n, o que significa que a próxima linha é a samelinha com corrente, NÃO podemos imprimi-lo de acordo com nossa idéia principal; caso contrário, o que significa que a linha atual é a ÚLTIMA aparência de todas as suas linhas consecutivas duplicadas, agora podemos usar o Pcomando para imprimir os caracteres no pattern spaceutilitário atual \n(\n também impresso).
D: usamos o Dcomando para excluir os caracteres no pattern spaceutilitário atual \n(\n também excluído), e o conteúdo de pattern spaceé a próxima linha.
e Dcomando forçará seda pular para seu FIRSTcomando$!N , mas NÃO lê a próxima linha do arquivo ou fluxo de entrada padrão.
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Explica:
leia uma nova linha do fluxo ou arquivo de entrada e imprima-a uma vez.
use o :loopcomando set a labelnamed loop.
use Npara ler a próxima linha no pattern space.
use s/^(.*)\n\1$/\1/para excluir a linha atual se a próxima linha for a mesma da linha atual, usamos o scomando para deleteexecutar a ação.
se o scomando for executado com sucesso, use a tloopforça de comando sedpara pular para o labelnomeado loop, que fará o mesmo loop para as próximas linhas util; não há linhas consecutivas duplicadas da linha que é latest printed; caso contrário, use o Dcomando para deletea linha que é a mesma com o latest-printed linee force sedpara pular para o primeiro comando, que é o pcomando, o conteúdo de current pattern spaceé a próxima nova linha.
uniq
só isso é suficiente.awk
, mas consumirá bastante recursos em arquivos maiores.Respostas:
seen
é uma matriz associativa para a qual o Awk passará todas as linhas do arquivo. Se uma linha não estiver na matriz, elaseen[$0]
será avaliada como falsa. O!
operador NOT é lógico e inverte o falso para verdadeiro. O Awk imprimirá as linhas em que a expressão é avaliada como verdadeira. Os++
incrementosseen
para que,seen[$0] == 1
após a primeira vez que uma linha seja encontradaseen[$0] == 2
, e assim por diante.O Awk avalia tudo menos
0
e""
(string vazia) como true. Se uma linha duplicada for inserida,seen
ela!seen[$0]
será avaliada como falsa e a linha não será gravada na saída.fonte
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Em http://sed.sourceforge.net/sed1line.txt : (Por favor, não me pergunte como isso funciona ;-))
fonte
$!
peça é necessária? Nãosed 'N; /^\(.*\)\n\1$/!P; D'
faz a mesma coisa? Não consigo criar um exemplo em que os dois sejam diferentes na minha máquina (fwiw, tentei uma linha vazia no final com as duas versões e ambas estavam bem).[ -~]
representa um intervalo de caracteres ASCII de 0x20 (espaço) a 0x7E (til). Esses são considerados os caracteres ASCII imprimíveis (a página vinculada também possui 0x7F / delete, mas isso não parece correto). Isso faz com que a solução seja quebrada para quem não usa ASCII ou para quem digita, por exemplo, caracteres de tabulação. O mais portátil[^\n]
inclui muito mais caracteres ... todos, exceto um, de fato.One-liner Perl semelhante à solução awk de @ jonas:
Essa variação remove o espaço em branco à direita antes de comparar:
Essa variação edita o arquivo no local:
Essa variação edita o arquivo no local e faz um backup
file.bak
fonte
A linha única que Andre Miller postou acima funciona, exceto nas versões recentes do sed quando o arquivo de entrada termina com uma linha em branco e sem caracteres. No meu Mac, minha CPU gira.
Loop infinito se a última linha estiver em branco e não tiver caracteres :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Não trava, mas você perde a última linha
sed '$d;N; /^\(.*\)\n\1$/!P; D'
A explicação está no final da FAQ sed :
fonte
Uma maneira alternativa de usar o Vim (compatível com Vi) :
Exclua linhas duplicadas e consecutivas de um arquivo:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
Exclua linhas duplicadas, não consecutivas e não vazias de um arquivo:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
fonte
A primeira solução também é de http://sed.sourceforge.net/sed1line.txt
a ideia central é:
Explica:
$!N;
: se a linha atual NÃO for a última, useN
comando para ler a próxima linhapattern space
./^(.*)\n\1$/!P
: se o conteúdo da correntepattern space
é doisduplicate string
separados por\n
, o que significa que a próxima linha é asame
linha com corrente, NÃO podemos imprimi-lo de acordo com nossa idéia principal; caso contrário, o que significa que a linha atual é a ÚLTIMA aparência de todas as suas linhas consecutivas duplicadas, agora podemos usar oP
comando para imprimir os caracteres nopattern space
utilitário atual\n
(\n
também impresso).D
: usamos oD
comando para excluir os caracteres nopattern space
utilitário atual\n
(\n
também excluído), e o conteúdo depattern space
é a próxima linha.D
comando forçarásed
a pular para seuFIRST
comando$!N
, mas NÃO lê a próxima linha do arquivo ou fluxo de entrada padrão.A segunda solução é fácil de entender (por mim):
a ideia central é:
Explica:
:loop
comando set alabel
namedloop
.N
para ler a próxima linha nopattern space
.s/^(.*)\n\1$/\1/
para excluir a linha atual se a próxima linha for a mesma da linha atual, usamos os
comando paradelete
executar a ação.s
comando for executado com sucesso, use atloop
força de comandosed
para pular para olabel
nomeadoloop
, que fará o mesmo loop para as próximas linhas util; não há linhas consecutivas duplicadas da linha que élatest printed
; caso contrário, use oD
comando paradelete
a linha que é a mesma com olatest-printed line
e forcesed
para pular para o primeiro comando, que é op
comando, o conteúdo de currentpattern space
é a próxima nova linha.fonte
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
Isso pode ser alcançado usando awk. A
linha abaixo exibirá valores exclusivos
Você pode gerar esses valores exclusivos para um novo arquivo
o novo arquivo uniq_file_name conterá apenas valores exclusivos, sem duplicatas
fonte
Exclui as linhas duplicadas usando o awk.
fonte
cat
é inútil. De qualquer forma,uniq
já faz isso por si só e não exige que a entrada seja exatamente uma palavra por linha.