Rimuovi le linee duplicate senza ordinare

Ho uno script di utilità in Python:

#!/usr/bin/env python import sys unique_lines = [] duplicate_lines = [] for line in sys.stdin: if line in unique_lines: duplicate_lines.append(line) else: unique_lines.append(line) sys.stdout.write(line) # optionally do something with duplicate_lines 

Questa semplice funzionalità (uniq senza la necessità di ordinare prima, l’ordine stabile) deve essere disponibile come semplice utility UNIX, non è vero? Forse una combinazione di filtri in un tubo?

Motivo della richiesta: bisogno di questa funzionalità su un sistema su cui non posso eseguire python da nessuna parte

Il blog UNIX Bash Scripting suggerisce :

 awk '!x[$0]++' 

Questo comando sta dicendo awk quali linee stampare. La variabile $0 contiene l’intero contenuto di una linea e le parentesi quadre sono l’accesso alla matrice. Quindi, per ogni riga del file, il nodo dell’array x viene incrementato e la linea stampata se il contenuto di quel nodo non era ( ! ) Precedentemente impostato.

Una risposta tardiva – ho appena trovato un duplicato di questo – ma forse vale la pena aggiungere …

Il principio alla base della risposta di @ 1_CR può essere scritto in modo più conciso, usando cat -n invece di awk per aggiungere numeri di riga:

 cat -n file_name | sort -uk2 | sort -nk1 | cut -f2- 
  • Usa cat -n per anteporre i numeri di riga
  • Usa sort -u rimuovere i dati duplicati
  • Usa sort -n per ordinare per numero anteposto
  • Usa il cut per rimuovere la numerazione delle linee

La soluzione di Michael Hoffman sopra è breve e dolce. Per i file di dimensioni maggiori, un approccio di trasformazione Schwartziano che prevede l’aggiunta di un campo indice mediante awk seguito da più cicli di ordinamento e uniq comporta un minore sovraccarico della memoria. Il seguente frammento funziona in bash

 awk '{print(NR"\t"$0)}' file_name | sort -t$'\t' -k2,2 | uniq --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t' 

Grazie 1_CR! Avevo bisogno di un “uniq -u” (rimuovere completamente i duplicati) piuttosto che uniq (lasciare una copia dei duplicati). Le soluzioni awk e perl non possono essere modificate per fare ciò, la tua lattina! Potrei aver anche avuto bisogno dell’uso di memoria più basso dal momento che sarò univoco come 100.000.000 di linee 8-). Nel caso in cui qualcuno ne avesse bisogno, ho appena messo un “-u” nella parte uniq del comando:

 awk '{print(NR"\t"$0)}' file_name | sort -t$'\t' -k2,2 | uniq -u --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t' 

Per rimuovere il duplicato da 2 file:

 awk '!a[$0]++' file1.csv file2.csv 

Volevo solo rimuovere tutti i duplicati sulle seguenti righe, non ovunque nel file. Quindi ho usato:

 awk '{ if ($0 != PREVLINE) print $0; PREVLINE=$0; }' 

il comando uniq funziona in un alias anche http://man7.org/linux/man-pages/man1/uniq.1.html

Ora puoi dare un’occhiata a questo piccolo strumento scritto in Rust: uq .

Esegue il filtraggio dell’unicità senza dover prima ordinare l’input, quindi può essere applicato su un stream continuo.