Vorrei rimuovere da una determinata colonna ($ 2 nell'esempio) i campi duplicati (separati da virgole).
File di input:
A 1,2,3,4
B 4,5,6,3
C 2,15
Uscita prevista:
A 1,2,3,4
B 5,6
C 15
Risposta accettata:
perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e'
Puoi eseguire quanto sopra in questo modo:
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
Come funziona
Prima chiamata a perl
con -lpe
fa le seguenti 3 cose.
-l[octal]
abilita l'elaborazione di fine riga, specifica il terminatore di riga-p
assume loop come -n ma stampa anche la riga, come sed-e program
una riga di programma (più -e consentite, omettere il file di programma)
Questo essenzialmente prende il file, rimuove le nuove righe, opera su una riga e quindi inserisce un carattere di nuova riga su di essa quando ha finito. Quindi è solo scorrere il file ed eseguire il nostro codice Perl contro ciascuno a turno.
Per quanto riguarda il codice Perl attuale:
s
indica un carattere di spaziatura (i cinque caratteri[ fnrt]
ev
nelle versioni più recenti diperl
, come[[:space:]]
).K
Mantieni il resto della K, non includerlo in $&S+
uno o più caratteri non presenti nell'insieme [ fnrtv]
Il join ",",
prenderà i risultati e si unirà nuovamente a ciascun campo in modo che sia separato da una virgola.
Il split ",", $&
prenderà le corrispondenze che sono state trovate da S+
e suddividili nei soli campi, senza la virgola.
Il grep {!$seen{$_}++}
prenderà il numero di ogni campo, lo aggiungerà all'hash, $seen{}
dove il numero di ogni campo è $_
mentre esaminiamo ciascuno di essi. Ogni volta che un numero di campo viene "visto", viene contato tramite il ++
operatore, $seen{$_}++
.
Il grep{!$seen{$_}++}
restituirà un valore di campo se è stato visto solo una volta.
Modificato per vedere cosa sta succedendo
Se usi questo abominio modificato puoi vedere cosa sta succedendo mentre questa battuta di Perl si sposta attraverso le linee del file.
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile
keys: 4 1 3 2 | vals: 1 1 1 1
A 1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B 5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C 15
Questo ti mostra il contenuto di $seen{}
al termine dell'elaborazione di una riga del file. Prendiamo la seconda riga del file.
B 4,5,6,3
Ed ecco come la mia versione modificata mostra quella riga come:
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
Quindi questo significa che abbiamo visto il campo n. 6 (1 volta), il campo n. 4 (2 volte), ecc. e il campo n. 5 (1 volta). Quindi, quando grep{...}
restituisce i risultati restituirà risultati da questo array solo se era presente in questa riga (4,5,6,3) e se l'abbiamo visto solo 1 volta (6,1,15,5). L'intersezione di questi 2 elenchi è (5,6) e quindi questo è ciò che viene restituito da grep
.
Riferimenti
- perlre – perldoc.perl.org