Valor de índices duplicados na matriz

2

Dados os arquivos abaixo:

arquivo1:

7997,1
7997,2
7997,3
5114,1
5114,2

arquivo2:

7997,52,
5114,12,
4221,52,

Como posso criar uma matriz a partir do primeiro arquivo que tenha a primeira coluna como índices e a segunda como valores a serem comparados com dados em file2 no awk?

Algo parecido com isto:

cat file1 file2 | awk -F, '{if(NF==2){arr[$1]=$2}else{if(arr[$1]){print arr[$1]","$0}}}'

Onde a saída desejada seria:

1,2,3,7997,52
1,2,5114,12
    
por Eng7 26.11.2015 / 11:03

3 respostas

2

Aqui está uma maneira:

$ awk -F, -vOFS=, 'NR==FNR{a[$1]=a[$1]","$2; next} 
                   ($1 in a){print a[$1],$0}' file1 file2 | 
    sed 's/^,\(.*\),$//'
1,2,3,7997,52
1,2,5114,12

Explicação

  • -F, -vOFS=, : define o separador do campo de entrada ( -F ) e o separador do campo de saída ( -vOFS , essa é a cadeia inserida entre cada valor impresso quando você executa print $1,$2 ) como vírgula .

  • NR==FNR{a[$1]=a[$1]","$2; next} : FNR é o número da linha do arquivo atual e NR é o número da linha da entrada. Quando awk recebe dois arquivos para ler, essas variáveis serão iguais apenas durante a leitura do primeiro arquivo. Portanto, o primeiro bloco, NR==FNR{} , só será executado enquanto o primeiro arquivo estiver sendo lido.

    O código neste bloco criará o array a com o primeiro campo como um índice. Cada vez que o bloco é executado, ele anexa uma vírgula e o valor do segundo campo ao que estiver armazenado na matriz no índice de $1 . O next pula para a próxima linha de entrada sem continuar o script, assim o segundo bloco não será executado para o primeiro arquivo.

    Desde a primeira vez que ele será executado, a[$1] estará vazio, isso adicionará uma vírgula extra ao início da matriz. Nós removemos isso com o sed no final.

  • ($1 in a){print a[$1],$0} : agora estamos no segundo arquivo. Se o primeiro campo dessa linha for um índice na matriz a , imprima o valor associado a esse índice em a e a linha atual ( $0 ).

  • sed 's/^,\(.*\),$//' : corresponde à primeira vírgula da linha ( ^, ) e, em seguida, usa parênteses para capturar tudo, exceto a última vírgula ( \(.*\),$ ). A coisa toda é então substituída pelo padrão capturado ( ). O resultado é que simplesmente remove a primeira e a última vírgula de cada linha. Isso é necessário para remover a vírgula extra adicionada no início da linha pelo script awk e a vírgula extra incluída no final de cada linha em file2 . Estou removendo o último, já que você também não o mostra na saída desejada.

por 26.11.2015 / 11:33
2

Você pode usar as variáveis FNR e NR para conseguir isso.

awk -F "," '{
  if(FNR==NR){
    if (a[$1] != ""){
      a[$1]=a[$1]","$2
      }
    else{
      a[$1]=$2
      }
    }
    else{
      if (a[$1]!= ""){
        print a[$1]","$1","$2
        }
      }
    }' file1 file2
    
por 26.11.2015 / 11:33
1

A partir da resposta perfeitamente boa de Jijin P e apertando um pouco a lógica. Isso seria um comentário sobre a resposta dele originalmente, então ficou muito longo (e é uma resposta válida) então aqui vai:

awk 'BEGIN {
  FS = ","
  OFS = ","
}

FNR == NR {
  if ($1 in a) {
    a[$1] = a[$1] OFS $2
  } else {
    a[$1] = $2
  }
  next
}

$1 in a {
  print a[$1], $1, $2
}' file1 file2

Em geral, é melhor usar if ($x in myarray) em vez de if (myarray[$x] != "") , a menos que você tenha uma razão específica para não. Se você quer apenas ter certeza de que o elemento da matriz não foi criado, use a primeira versão. Se você sabe que foi criado e quer garantir que não seja uma string em branco, use o segundo. O truque com o segundo é que apenas nomeando o elemento da matriz myarray[$x] , mesmo no contexto da verificação de seu valor, o elemento é criado silenciosamente. Isso pode bagunçar você em alguns casos quando você vai imprimir o array usando for (index in myarray) .

E, ao usar print var1 "," var2 "," var3 , esse é o caso de uso exato para o qual existe OFS (separador de campo de saída). A configuração do OFS no bloco BEGIN facilita e agiliza a alteração do formato de saída do script inteiro.

Por fim, ao executar uma ação para o primeiro arquivo e uma ação diferente para segundo / outros arquivos, um bloco padronizado com FNR == NR terminando em uma instrução next é mais limpo em minha opinião do que um bloco if / else. / p>     

por 27.11.2015 / 01:24