Combina colunas correspondentes de dois arquivos ou usa o valor padrão

1

Estou tentando reescrever um texto de arquivo, main.txt com alterações de changes.txt . O arquivo main.txt é assim:

word_1 genre_A
word_2 genre_A
word_3 genre_B
word_4 genre_C
word_5 genre_A

E o changes.txt é assim:

genre_A root_A
genre_C root_C

Eu gostaria de alterar os que estão em changes.txt em main.txt para obter algo assim:

word_1 root_A
word_2 root_A
word_3 genre_B
word_4 root_C
word_5 root_A

Esses arquivos são muito grandes, então eu preciso de um método automático.

Eu tentei carregar todas as variáveis de changes.txt em uma matriz awk e, depois, imprimir o mesmo que era se fosse diferente, e alterar se é o mesmo. Algo parecido com isto:

NR==FNR{a[$1,$2]++;next}  $3==a[$1] {print $1,$2,a[$2]} $3!=a[$1] {print $1,$2,$3}

Mas estou fazendo algo errado. Eu também ouço isso pode ser feito com o comando join , mas eu não estou acostumado com isso, então seria útil explicar cada parte.

Obrigado por todos.

    
por cloudy_fog 27.06.2016 / 15:16

3 respostas

1

Você mencionou o comando join , mas, nesse caso, não é fácil usá-lo. Pelo menos não por si só.

Vamos tentar juntar os dois arquivos. Para fazer isso, os arquivos precisam ser classificados primeiro no campo de junção. Vamos usar o segundo campo do primeiro arquivo e o primeiro campo (que é o padrão para join ) do segundo arquivo:

$ sort -k2 -o main.txt main.txt
$ sort -k1 -o changes.txt changes.txt

Então fazemos a junção:

$ join -1 2 main.txt changes.txt
genre_A word_1 root_A
genre_A word_2 root_A
genre_A word_5 root_A
genre_C word_4 root_C

Estamos perdendo uma linha. Tente novamente e diga join para também as linhas de saída de main.txt que seriam deixadas de fora (porque elas não correspondem a nada em changes.txt ):

$ join -1 2 -a 1 main.txt changes.txt
genre_A word_1 root_A
genre_A word_2 root_A
genre_A word_5 root_A
word_3 genre_B
genre_C word_4 root_C

Observe que não podemos pedir ao join para enviar apenas algumas das colunas de um dos arquivos porque a segunda coluna na saída desejada contém uma mistura de dados dos dois arquivos.

No entanto, com a saída acima, podemos extrair as duas últimas colunas usando um simples script awk (desejo que cut possa ter especificações de campo negativas para obter colunas da direita!):

$ join -1 2 -a 1 main.txt changes.txt | awk '{ print $(NF-1), $NF }'
word_1 root_A
word_2 root_A
word_5 root_A
word_3 genre_B
word_4 root_C

Canalize isso para sort para classificação:

$ join -1 2 -a 1 main.txt changes.txt | awk '{ print $(NF-1), $NF }' | sort
word_1 root_A
word_2 root_A
word_3 genre_B
word_4 root_C
word_5 root_A
    
por 27.06.2016 / 17:02
1
Comando

Awk :

awk 'NR==FNR {a[$1]=$2;next} {if ($2 in a) print $1,a[$2]; else print $1,$2}' changes.txt main.txt

Saída:

word_1 root_A
word_2 root_A
word_3 genre_B
word_4 root_C
word_5 root_A
    
por 27.06.2016 / 15:34
1
Método

Classificar e juntar (mas não awk ):

sort -k2    main.txt >    main_sort.txt ; \
sort -k1 changes.txt > changes_sort.txt ; \
{ join      -i  -1 2 -2 1  -o 1.1 2.2  main_sort.txt changes_sort.txt ; \
  join -v 1 -i  -1 2 -2 1  -o 1.1 1.2  main_sort.txt changes_sort.txt ; } | \
  sort -g | column -t

Saída:

word_1  root_A
word_2  root_A
word_3  genre_B
word_4  root_C
word_5  root_A

Explicação ...

join requer dois arquivos com campos classificados. Como cada arquivo é grande e precisa ser usado duas vezes, é mais eficiente classificar os dois arquivos antecipadamente. Suponha que todos os arquivos estejam tab delimitados.

Classifique main.txt pelo campo 2nd (gênero) e changes.txt pelo primeiro campo (raiz):

sort -k2    main.txt >    main_sort.txt
sort -k1 changes.txt > changes_sort.txt

Passe 1, imprima as linhas com correspondências:

join -i -1 2 -2 1 -o 1.1 2.2 main_sort.txt changes_sort.txt

Que gera 4 linhas, classificadas pelo campo raiz :

word_1 root_A
word_2 root_A
word_5 root_A
word_4 root_C

Os join sinalizadores "- 1 2 -2 1" informam para juntar o segundo do primeiro arquivo em> para o campo primeiro do arquivo. Os sinalizadores "- o 1.1 2.2" informam para imprimir o campo primeiro do primeiro arquivo para o campo do segundo arquivo.

Passe 2, use o sinalizador "- v 1" para exibir a linha ausente do arquivo primeiro :

join -v 1 -i -1 2 -2 1  -o 1.1 1.2  main_sort.txt changes_sort.txt

Saída:

word_3 genre_B

Depois disso, as saídas de ambos são combinadas e depois classificadas (veja o início da resposta).

    
por 27.06.2016 / 17:09