Присоединитесь к двум файлам, добавив значения в определенных столбцах

Как я могу добавить еще 2 значения после соответствия значениям из 2 столбцов в другом файле точно так же, как VLOOKUP?

Образец ниже. Значение в столбце 6 и 7 от file1 при соответствии столбцу 1 и 2 от file2 добавит столбец 9 и 10 в file1 со значением столбца 3 и 4 от file2.

file1.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

file2.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

Спасибо,

3
задан 2 May 2018 в 17:03

5 ответов

Использовать awk

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

и удалить пустые строки из вывода:

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

или немного пробела и разумных имен переменных имеют большое значение для удобочитаемости. Кроме того, используйте в своих интересах использование запятой в ключе массива

awk '
    NR == FNR {value[$1,$2] = $3 OFS $4; next} 
    {print $0, value[$6,$7]}
' file2.txt file1.txt

  • NR установлен на 1, когда первая запись читала awk и увеличивающий для каждый следующие записи, читая или на сингле или на нескольких входных файлах до всего законченного чтения.
  • FNR установлен на 1, когда первая запись, считанная awk и увеличивающий для каждый следующие записи, читающие в текущем файле и, сбросила назад к 1 для следующего входного файла если несколько входных файлов.
  • так NR == FNR всегда истинное условие, и блок, сопровождаемый этим, выполнит действия с первым файлом только.

  • seen связанный массив awk с сочетанием клавиш 1 column$ и 2 column$ со значением 3 column$ и 4 column$.

  • next маркерные пропуски к выполняющемуся отдыху команд и они только выполнятся на самом деле для следующего файла (файлов) кроме сначала.

  • NF; предварительная установка Нумерует Полей в записи, где поля известны и разделяются с Разделителем полей FS; так FS между столбцами там привык к неповрежденному разделитель полей, или Вы могли использовать запятую , в массиве вместо этого.

  • так это NF{ print $0, seen[$6FS$7] }, распечатайте текущую запись $0 в file1 и значении, соответствовавшем 6 column$ и 7 column$, существующим в массиве, замеченном, когда это не было пустой строкой.

6
ответ дан 1 December 2019 в 12:56

Я знаю, что Вы не попросили решение для базы данных, но если у Вас, оказывается, есть сервер MySQL вокруг, вот то, как сделать это:

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;

(Я должен был разделить пустые строки также),

Результат:

+------+------+------+------+------+------+------+------+------+------+
| 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)
4
ответ дан 1 December 2019 в 12:56

Ответ на ответ @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"

или almost-one-liner способом:

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
4
ответ дан 1 December 2019 в 12:56

удар: Я брал на себя смелость удаления пустых строк из файлов.

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
2
ответ дан 1 December 2019 в 12:56

Это будет работать, хотя я вполне уверен, кто-то придумает намного лучшую остроту awk решение.

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

Волшебство происходит в строке:

file1_matching_line=[...]

Найдите номер строки (-n) из всех случаев поля 1 и 2 Файла 2

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

в рамках output.txt, который является копией file1.txt

<(cut -d' ' -f6,7 output.txt)
1
ответ дан 1 December 2019 в 12:56

Другие вопросы по тегам:

Похожие вопросы: