perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e'
Você pode executar o acima da seguinte forma:
$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
Como funciona
A primeira chamada de perl com -lpe faz as três coisas a seguir.
-
-l[octal]habilita o processamento final da linha, especifica o terminador de linha -
-passume loop como -n mas linha de impressão também, como sed -
-e programuma linha de programa (vários -e é permitido, omitir arquivo de programa)
Isso essencialmente pega o arquivo, retira as novas linhas, opera em uma linha e depois coloca um caractere de nova linha de volta nele quando terminar. Então, é apenas passar pelo arquivo e executar nosso código Perl contra cada um deles.
Quanto ao código Perl real:
-
\ssignifica um caractere de espaçamento (os cinco caracteres[ \f\n\r\t]e\vnas versões mais recentes deperl, como[[:space:]]). -
\KGuarde as coisas restantes de \ K, não inclua em $ & -
\S+um ou mais caracteres que não estão no conjunto [\ f \ n \ r \ t \ v]
O join ",", vai obter os resultados e voltar a cada campo para que seja separado por uma vírgula.
O split ",", $& pegará as correspondências encontradas pelo \S+ e as dividirá em apenas os campos, sem a vírgula.
O grep {!$seen{$_}++} pegará o número de cada campo e o adicionará ao hash, $seen{} , onde o número de cada campo será $_ conforme passarmos por cada um deles. Cada vez que um número de campo é "visto", é contado pelo operador ++ , $seen{$_}++ .
O grep{!$seen{$_}++} retornará um valor de campo se for visto apenas uma vez.
Modificado para ver o que está acontecendo
Se você usar essa abominação modificada, você pode ver o que está acontecendo enquanto esse forro de Perl se move pelas linhas do arquivo.
$ perl -lpe 's/\s\K\S+/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
Isso mostra o conteúdo de $seen{} no final do processamento de uma linha do arquivo. Vamos pegar a segunda linha do arquivo.
B 4,5,6,3
E aqui está o que minha versão modificada mostra nessa linha como:
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
Então, isso está dizendo que vimos o campo # 6 (1 vez), o campo # 4 (2 vezes), etc. e o campo # 5 (1 vez). Então, quando grep{...} devolve os resultados, ele só retornará resultados desta matriz se estiver presente nesta linha (4,5,6,3) e se tivermos visto apenas 1 vez (6,1,15,5 ). A interseção dessas 2 listas é (5,6) e é isso que é retornado por grep .