Une dois arquivos, adicionando valores em certas colunas

3

Como posso adicionar outros 2 valores depois de combinar os valores de 2 colunas em outro arquivo como VLOOKUP?

Amostra abaixo. Valor na coluna 6 & 7 do arquivo1, quando combinado com a coluna 1 & 2 do arquivo2, adicionará a coluna 9 & 10 no arquivo1 com o valor da coluna 3 & 4 do arquivo2.

arquivo1.txt

1 1 1 1 1 5 9 1

2 2 2 2 2 7 8 2

3 3 3 3 3 7 7 3

4 4 4 4 4 8 6 4

arquivo2.txt

5 9 A B

8 6 E F

7 7 G H

7 8 C D

output.txt

1 1 1 1 1 5 9 1 A B

2 2 2 2 2 7 8 2 C D

3 3 3 3 3 7 7 3 G H

4 4 4 4 4 8 6 4 E F

Obrigado,

    
por pawana 02.05.2018 / 15:36

5 respostas

6

Use awk

awk 'NR==FNR{ seen[$1FS$2]=$3FS$4; next } { print $0, seen[$6FS$7] }' file2 file1

e para excluir linhas vazias da saída:

awk 'NR==FNR{ seen[$1FS$2]=$3FS$4; next } NF{ print $0, seen[$6FS$7] }' file2 file1

ou um pouco de espaços em branco e nomes de variáveis sensíveis são muito úteis para a legibilidade. Além disso, aproveite o uso de uma vírgula na chave da matriz

awk '
    NR == FNR {value[$1,$2] = $3 OFS $4; next} 
    {print $0, value[$6,$7]}
' file2.txt file1.txt
  • NR é definido como 1 quando o primeiro registro lido por awk e incrementando para cada próximo registro de leitura em arquivos de entrada únicos ou múltiplos até que todas as leituras sejam concluídas.
  • FNR é definido como 1 quando o primeiro registro for lido por awk e incrementado para cada próximo registro de leitura no arquivo atual e redefinido como 1 para o próximo arquivo de entrada, se houver vários arquivos de entrada.
  • então NR == FNR é sempre uma condição verdadeira e o bloco seguido por isso executará ações apenas no primeiro arquivo.

  • O seen é uma matriz awk associada com a combinação de teclas da coluna $ 1 e coluna $ 2 com o valor da coluna $ 3 e da coluna $ 4.

  • O token next pula para executar o restante dos comandos e eles serão executados apenas para o (s) próximo (s) arquivo (s), exceto o primeiro.

  • NF ; predefinindo N umber de F campos em um registro onde os campos são conhecidos e separados por um F campo S eparator% código%; então FS entre as colunas usadas para intacto o separador de campos ou você poderia usar comma FS no array.

  • assim, este , , imprime o registro atual NF{ print $0, seen[$6FS$7] } no arquivo1 e o valor corresponde à coluna $ 6 e coluna $ 7 presentes no array visto quando não era uma linha vazia.

por devWeek 02.05.2018 / 16:18
5

Eu sei que você não pediu uma solução de banco de dados, mas se você tem um servidor MySQL por perto, aqui está como fazer:

create table file1 (c1 int, c2 int, c3 int, c4 int, c5 int, c6 int, c7 int, c8 int);
create table file2 (c1 int, c2 int, c3 char, c4 char);
load data infile 'file1' into table file1 fields terminated by ' ';
load data infile 'file2' into table file2 fields terminated by ' ';
select f1.*, f2.c3, f2.c4 from file1 as f1 
    join file2 as f2 
        on f1.c6 = f2.c1 and f1.c7 = f2.c2 
    order by f1.c1;

(Eu tive que tirar as linhas em branco também)

Resultado:

+------+------+------+------+------+------+------+------+------+------+
| c1   | c2   | c3   | c4   | c5   | c6   | c7   | c8   | c3   | c4   |
+------+------+------+------+------+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |    5 |    9 |    1 | A    | B    |
|    2 |    2 |    2 |    2 |    2 |    7 |    8 |    2 | C    | D    |
|    3 |    3 |    3 |    3 |    3 |    7 |    7 |    3 | G    | H    |
|    4 |    4 |    4 |    4 |    4 |    8 |    6 |    4 | E    | F    |
+------+------+------+------+------+------+------+------+------+------+
4 rows in set (0,00 sec)
    
por Jos 02.05.2018 / 16:31
5

Respondendo à resposta do @ Jos: sqlite

db=$(mktemp)
sqlite3 "$db" <<'END'
create table f1 (v1 text,v2 text,v3 text,v4 text,v5 text,v6 text,v7 text,v8 text);
create table f2 (v1 text,v2 text,v3 text,v4 text);
.separator " "
.import file1.txt f1
.import file2.txt f2
select f1.*, f2.v3, f2.v4 from f1,f2 where f1.v6=f2.v1 and f1.v7=f2.v2;
END
rm "$db"

ou de um modo quase linear:

sqlite3 -separator " "  <<'END'
create table f1 (v1, v2, v3, v4, v5, v6, v7, v8 );
create table f2 (v1, v2, v3, v4);
.import file1.txt f1
.import file2.txt f2
select f1.*, f2.v3, f2.v4 from f1,f2 where f1.v6=f2.v1 and f1.v7=f2.v2;
END
    
por glenn jackman 17.05.2018 / 11:52
2

bash: tomei a liberdade de remover linhas em branco dos arquivos.

declare -A keys
while read -r k1 k2 value; do 
    keys[$k1,$k2]=$value
done < file2.txt
while read -ra fields; do 
    key="${fields[5]},${fields[6]}"; 
    echo "${fields[*]} ${keys[$key]}"
done < file1.txt
1 1 1 1 1 5 9 1 A B
2 2 2 2 2 7 8 2 C D
3 3 3 3 3 7 7 3 G H
4 4 4 4 4 8 6 4 E F
    
por glenn jackman 02.05.2018 / 16:29
1

Isso funcionará, embora eu tenha certeza de que alguém criará uma solução awk de uma linha muito melhor.

cp file1.txt output.txt &&
while read -r file2_line; do
    # Empty line --> continue
    [[ -z "$file2_line" ]] && continue
    # Find matching line
    file1_matching_line=$(grep -n "$(echo "$file2_line" | cut -d' ' -f 1,2)" <(cut -d' ' -f6,7 output.txt) | grep -Po "^[0-9]+");
    # no find? continue!
    [[ ! $? -eq 0 ]] && continue
    # Add the fields 3 and 4 of file2 to the end of the matching line of output.txt
    echo "$file1_matching_line" | while read -r ml; do
        sed -i "${ml}s/$/ $(echo "$file2_line" | cut -d' ' -f 3,4)/" output.txt
    done
done < file2.txt && cat output.txt

A mágica acontece na linha:

file1_matching_line=[...]

Encontre o número da linha ( -n ) de todas as ocorrências dos campos 1 e 2 do Arquivo 2

$(echo "$file2_line" | cut -d' ' -f 1,2)

dentro do output.txt, que é uma cópia do arquivo1.txt

<(cut -d' ' -f6,7 output.txt)
    
por RoVo 02.05.2018 / 16:08