Mesclar listas com base em valores duplicados

2

Eu classifiquei dados como este:

a
a f
b
c
c e
d
f z

Essencialmente, essas linhas são listas de aliases para a mesma coisa e precisam ser mescladas. Isso é simplificado. Caso seja importante no caso real, estou lidando com caminhos de arquivos que se moveram e precisam saber quais caminhos de arquivo são essencialmente os mesmos. A entrada tem 1 coluna para o arquivo inicial e 2 colunas quando um arquivo foi renomeado. Procurando por uma saída assim:

a f z
b
c e
d

Isto é para um script bash em um sistema Linux típico, portanto, qualquer ferramenta padrão na maioria das vezes serve. Eu tentei alguns scripts awk de outras questões que lidam com este assunto até agora e não encontrei bons resultados.

    
por kcghost 08.03.2018 / 16:41

2 respostas

2

Awk solução:

awk '{ 
         if (NF == 2) {
             if ($1 in r) { 
                 a[r[$1]] = a[r[$1]] OFS $2; next 
             } 
             a[$1] = $2; r[$2] = $1; 
         } 
         else a[$1]; 
     }
     END{ for (i in a) print i, a[i]  }' file
  • NF == 2 - condição indicando um registro com 2 campos ( NF - número total de campos)
  • a - matriz que contém nomes de arquivos "independentes" (que não foram renomeados) como b e d ou relação entre o nome do arquivo inicial e sua versão renomeada (por exemplo, a -> f )
  • r - matriz que contém a relação oposta "nome do arquivo renomeado" - > "nome do arquivo inicial" (por exemplo, f -> a )

A saída:

a f z
b 
c e
d 

No caso, se algum nome de arquivo pudesse ter sido renomeado mais de uma vez - use a seguinte solução estendida:

awk '{ 
         if (NF == 2) {
             if ($1 in r) { 
                 a[r[$1]] = a[r[$1]] OFS $2; r[$2] = r[$1];
             } 
             else { a[$1] = $2; r[$2] = $1 } 
         } 
         else a[$1]; 
     }
     END{ for (i in a) print i, a[i]  }' file
    
por 08.03.2018 / 17:02
1
gawk '
{
    arr[cnt][0] = $1    
    arr[cnt++][1] = $2  
}
END {
    for(i = 0; i < cnt; i++) {
        if(!arr[i][0]) continue

        next_name = arr[i][0]

        for(j = i; j < cnt; j++) {
            if(arr[j][0] != next_name) continue

            if(arr[j][1]) {
                next_name = arr[j][1]
                delete arr[j]
            }
            printf "%s ", next_name

        }
        print ""
    }
}' cnt=0 input.txt

Entrada (complicada para testes)

u
a
a f
b
c
c e
d
c
f g
g a
a i
i j
a
a z
z w

Resultado

u 
a f g a i j 
b 
c e 
d 
c 
a z w 
    
por 10.03.2018 / 11:37