Une dois arquivos, correspondentes em uma coluna, com repetições

7

Como posso obter dois arquivos A e B e colocar um resultado assim:

Arquivo A:

001 Apple, CA
020 Banana, CN
023 Apple, LA
045 Orange, TT
101 Orange, OS
200 Kiwi, AA

Arquivo B:

01-Dec-2013 01.664  001     AAA CAC 1083
01-Dec-2013 01.664  020     AAA CAC 0513
01-Dec-2013 01.668  023     AAA CAC 1091
01-Dec-2013 01.668  101     AAA CAC 0183
01-Dec-2013 01.674  200     AAA CAC 0918
01-Dec-2013 01.674  045     AAA CAC 0918
01-Dec-2013 01.664  001     AAA CAC 2573
01-Dec-2013 01.668  101     AAA CAC 1091
01-Dec-2013 01.668  020     AAA CAC 6571
01-Dec-2013 01.668  023     AAA CAC 2148
01-Dec-2013 01.674  200     AAA CAC 0918
01-Dec-2013 01.668  045     AAA CAC 5135

Resultado:

01-Dec-2013 01.664  001     AAA CAC 1083    Apple, CA
01-Dec-2013 01.664  020     AAA CAC 0513    Banana, CN
01-Dec-2013 01.668  023     AAA CAC 1091    Apple, LA
01-Dec-2013 01.668  101     AAA CAC 0183    Orange, OS
01-Dec-2013 01.674  200     AAA CAC 0918    Kiwi, AA
01-Dec-2013 01.674  045     AAA CAC 0918    Orange, TT
01-Dec-2013 01.664  001     AAA CAC 2573    Apple, CA
01-Dec-2013 01.668  101     AAA CAC 1091    Orange, OS
01-Dec-2013 01.668  020     AAA CAC 6571    Banana, CN
01-Dec-2013 01.668  023     AAA CAC 2148    Apple, LA
01-Dec-2013 01.674  200     AAA CAC 0918    Kiwi, AA
01-Dec-2013 01.668  045     AAA CAC 5135    Orange, TT

(arquivo A: o número deve corresponder ao número do meio do arquivo B)

Existe alguma maneira possível de fazer isso?

    
por JOSS 25.01.2014 / 04:25

5 respostas

5

Uma solução simples com o awk:

awk -v FILE_A="file-A" -v OFS="\t" 'BEGIN { while ( ( getline < FILE_A ) > 0 ) { VAL = $0 ; sub( /^[^ ]+ /, "", VAL ) ; DICT[ $1 ] = VAL } } { print $0, DICT[ $3 ] }' file-B

Aqui está uma versão comentada:

awk -v FILE_A="file-A" -v OFS="\t" '
BEGIN {

  # Loop on the content of file-A
  # to put the values in a table

  while ( ( getline < FILE_A ) > 0 ){

     # Remove the index from the value
     VAL = $0
     sub( /^[^ ]+ /, "", VAL )

     # Fill the table
     DICT[ $1 ] = VAL
  }
}
{

  # Print the line followed by the
  # corresponding value
  print $0, DICT[ $3 ]

}' file-B
    
por 25.01.2014 / 14:14
3

Aqui está um script Bash que faz o que você está procurando. O script é chamado de mergeAB.bash .

#!/bin/bash

readarray A < fileA.txt 

i=0
while read -r B; do
  idx=$(( $i % ${#A[@]} ))

  printf "%s %s" "$B" "${A[$idx]}"
  #echo "i: $i | A#: ${#A[@]} | IDX: $idx"

  let i=i+1
done < fileB.txt

Quando você o executa:

$ ./mergeAB.bash 
01-Dec-2013 01.664  001     AAA CAC 1083 001 Apple, CA
01-Dec-2013 01.664  020     AAA CAC 0513 020 Banana, CN
01-Dec-2013 01.668  023     AAA CAC 1091 023 Apple, LA
01-Dec-2013 01.668  101     AAA CAC 0183 045 Orange, TT
01-Dec-2013 01.674  200     AAA CAC 0918 101 Orange, OS
01-Dec-2013 01.674  045     AAA CAC 0918 200 Kiwi, AA
01-Dec-2013 01.664  001     AAA CAC 2573 001 Apple, CA
01-Dec-2013 01.668  101     AAA CAC 1091 020 Banana, CN
01-Dec-2013 01.668  020     AAA CAC 6571 023 Apple, LA
01-Dec-2013 01.668  023     AAA CAC 2148 045 Orange, TT
01-Dec-2013 01.674  200     AAA CAC 0918 101 Orange, OS
01-Dec-2013 01.668  045     AAA CAC 5135 200 Kiwi, AA

Detalhes

A primeira coisa que fazemos é usar o comando readarray para ler o conteúdo de fileA.txt em uma matriz. Este é um recurso mais novo do Bash 4.x, então se você estiver usando uma versão mais antiga do Bash, você pode usar algo assim:

$ IFS=$'\n' read -d '' -r -a A < fileA.txt

O resto deste script é um pouco complexo, mas deixei um verboso echo no meio que você pode descomentar para ver o que está acontecendo.

$ ./mergeAB.bash | grep i:
i: 0 | A#: 6 | IDX: 0
i: 1 | A#: 6 | IDX: 1
i: 2 | A#: 6 | IDX: 2
i: 3 | A#: 6 | IDX: 3
i: 4 | A#: 6 | IDX: 4
i: 5 | A#: 6 | IDX: 5
i: 6 | A#: 6 | IDX: 0
i: 7 | A#: 6 | IDX: 1
i: 8 | A#: 6 | IDX: 2
i: 9 | A#: 6 | IDX: 3
i: 10 | A#: 6 | IDX: 4
i: 11 | A#: 6 | IDX: 5

O que está acontecendo aqui? Há um contador, $i , que usamos para contar cada linha de fileB.txt à medida que passamos por ela. Em seguida, calculamos $idx calculando a divisão do módulo do valor atual de $i e o número de linhas em fileA.txt .

NOTA: o comprimento da matriz A . Calculando $idx desta forma, podemos fazer um "loop" em torno de 0 a 5, depois 0 a 5 etc. Na saída de depuração acima, você pode ver isso com a coluna IDX: .

O restante do script é bastante padronizado, usando printf para imprimir as linhas concatenadas de fileB.txt com a linha correspondente de fileA.txt .

    
por 25.01.2014 / 07:05
2
$ cat b | while read b; do key=$(echo $b | awk '{print $3}'); /bin/echo -n "$b  "; grep -w $key a | cut -d\  -f2-; done
01-Dec-2013 01.664  001     AAA CAC 1083  Apple, CA
01-Dec-2013 01.664  020     AAA CAC 0513  Banana, CN
01-Dec-2013 01.668  023     AAA CAC 1091  Apple, LA
01-Dec-2013 01.668  101     AAA CAC 0183  Orange, OS
01-Dec-2013 01.674  200     AAA CAC 0918  Kiwi, AA
01-Dec-2013 01.674  045     AAA CAC 0918  Orange, TT
01-Dec-2013 01.664  001     AAA CAC 2573  Apple, CA
01-Dec-2013 01.668  101     AAA CAC 1091  Orange, OS
01-Dec-2013 01.668  020     AAA CAC 6571  Banana, CN
01-Dec-2013 01.668  023     AAA CAC 2148  Apple, LA
01-Dec-2013 01.674  200     AAA CAC 0918  Kiwi, AA
01-Dec-2013 01.668  045     AAA CAC 5135  Orange, TT

Eu suspeito que a construção awk possa ser feita de uma maneira mais elegante, mas parece funcionar.

    
por 25.01.2014 / 04:58
2

O utilitário join executa uma "união de igualdade" nos arquivos especificados e grava o resultado na saída padrão. O "campo de junção" é o campo em cada arquivo pelo qual os arquivos são comparados.

Em outras palavras, você tem dois arquivos que compartilham uma coluna. Você pode unir as linhas desses arquivos onde a coluna é igual.

Então, vamos tentar:

$ join -1 1 -2 3 a b
001 Apple, CA 01-Dec-2013 01.664 AAA CAC 1083
020 Banana, CN 01-Dec-2013 01.664 AAA CAC 0513
023 Apple, LA 01-Dec-2013 01.668 AAA CAC 1091
101 Orange, OS 01-Dec-2013 01.668 AAA CAC 0183
200 Kiwi, AA 01-Dec-2013 01.674 AAA CAC 0918

Sim, funciona. Mas não no formato que você especificou. Então, vamos trocar os arquivos:

$ join -1 3 -2 1 b a
001 01-Dec-2013 01.664 AAA CAC 1083 Apple, CA
020 01-Dec-2013 01.664 AAA CAC 0513 Banana, CN
023 01-Dec-2013 01.668 AAA CAC 1091 Apple, LA
101 01-Dec-2013 01.668 AAA CAC 0183 Orange, OS
200 01-Dec-2013 01.674 AAA CAC 0918 Kiwi, AA

Muito melhor. Ainda não está certo, já que o campo unido aparece primeiro. O Awk pode consertar isso:

$ join -1 3 -2 1 b a | awk '{print $2,$3,$1,$4,$5,$6,$7,$8}'
01-Dec-2013 01.664 001 AAA CAC 1083 Apple, CA
01-Dec-2013 01.664 020 AAA CAC 0513 Banana, CN
01-Dec-2013 01.668 023 AAA CAC 1091 Apple, LA
01-Dec-2013 01.668 101 AAA CAC 0183 Orange, OS
01-Dec-2013 01.674 200 AAA CAC 0918 Kiwi, AA

Então lá vai você. Os campos estão na mesma ordem. Em awk você pode usar printf ou inserir algumas abas, se você quiser obter o espaçamento exato, mas eu acho que você vai ter a idéia.

    
por 25.01.2014 / 09:37
0

Com uma matriz, conforme solicitado (inteiramente em bash ) ...

while read num loc; do A[0x$num]=$loc; done < A
while read B; do set -- $B; echo "${B} ${A[0x$3]}"; done < B

(funciona no bash v2)

A primeira linha carrega a matriz "A" do arquivo A. O bit 0x $ num é manter tudo na mesma base numérica, caso contrário os zeros à esquerda os tornam octal. A segunda linha lê cada linha do arquivo B (preservando espaços), define os argumentos posicionais dessa linha e finalmente imprime a linha mais a entrada indexada de "A".

    
por 25.01.2014 / 10:06