Mesclar dois arquivos classificados com base em valores de classificação no mesmo campo

0

Gostaria de combinar as linhas de dois arquivos classificados, não necessariamente do mesmo tamanho, mas com os mesmos campos de dados e com o mesmo cabeçalho, iniciando após um cabeçalho atualizado com base na ordem de uma determinada coluna. Por exemplo, o arquivo 1 é:

header 1
header 2
header 3

cat    4    aa
dog    5    ab
ostrich    10    cd
fish    13    cc

e o arquivo 2 é:

header 1
header 2
header 3

lemur    3    dd
alligator    4    ca
lemming    16    ad

e eu gostaria de 1) manter o cabeçalho idêntico, mas 2) classificar as seguintes linhas com base na coluna 2. A saída que eu gostaria é:

header 1
header 2
header 3

lemur    3    dd
cat    4    aa
alligator     4    ca
dog    5    ab
ostrich    10    cd
fish    13    cc
lemming     16    ad

Eu olhei, mas não consegui encontrar uma solução exatamente para essa situação usando awk ou join .

    
por itf 01.12.2016 / 04:50

3 respostas

1

Com um moderno (versão > 4.0) do GNU awk, você poderia fazer

awk '
  FNR>4 {a[$0]=$2; next}; 
  NR==FNR; 
  END {
    PROCINFO["sorted_in"] = "@val_num_asc"; 
    for (i in a) print i;
  }
' file1 file2

Explicação:

  • FNR>4 {a[$0]=$2; next}; cria uma matriz dos campos de classificação de linhas sem cabeçalho
  • NR==FNR; avalia TRUE apenas para o primeiro arquivo e só é alcançado para FNR>4 , fazendo com que as linhas de cabeçalho sejam impressas para o primeiro arquivo
  • PROCINFO["sorted_in"] = "@val_num_asc" classifica a matriz por valor (ou seja, campo armazenado $ 2)
  • for (i in a) print i imprime os índices da matriz classificada (que são as linhas de cabeçalho não armazenadas)

Teste

$ awk 'FNR>4 {a[$0]=$2; next}; NR==FNR; END {PROCINFO["sorted_in"] = "@val_num_asc"; for (i in a) print i;}' file1 file2
header 1
header 2
header 3

lemur    3    dd
cat    4    aa
alligator    4    ca
dog    5    ab
ostrich    10    cd
fish    13    cc
lemming    16    ad
    
por 01.12.2016 / 14:10
0

awk e join são as ferramentas erradas para isso.

sed '/^$/q' file1; sort -snmk2,2 <(sed '1,/^$/d' file1) <(sed '1,/^$/d' file2)
    
por 01.12.2016 / 05:12
0

Usando um shell que tem substituições de processo ( ksh93 , bash , ...) (veja o final de uma alternativa livre de substituição de processo):

cat <( head -n 3 file1 ) \
    <( sort -k2,2n <( tail -n +4 file1 | tr -s ' ' '\t' ) \
                   <( tail -n +4 file2 | tr -s ' ' '\t' ) | uniq )

Isso resultará em:

header 1
header 2
header 3

lemur   3       dd
alligator       4       ca
cat     4       aa
dog     5       ab
ostrich 10      cd
fish    13      cc
lemming 16      ad

O comando concatena as linhas de cabeçalho de file1 com o resultado de uma operação de classificação. A classificação é feita numericamente no segundo campo de alguma entrada, e quaisquer linhas duplicadas (jacarés, lêmures e lemingues) são removidos com uniq do resultado.

A entrada para classificar será o conteúdo sem cabeçalho de file1 e file2 , passado por tr para substituir espaços consecutivos por guias únicas (havia um número ímpar de espaços entre as colunas nos dados de exemplo ).

O resultado é delimitado por tabulações.

Uma maneira equivalente, usando as mesmas ferramentas:

cat <( head -n 3 file1 ) \
    <( sort -k2,2n <( cat <( tail -n +4 file1 ) \
                          <( tail -n +4 file2 ) | tr -s ' ' '\t' ) | uniq )

Sem o cat s e as substituições do processo:

{ head -n 3 file1;
    { tail -n +4 file1; tail -n +4 file2; } | tr -s ' ' '\t' | sort -k2,2n | uniq; }
    
por 21.09.2017 / 19:54

Tags