найти и заменить на значение в другом файле

Вопрос или проблема

У меня есть два файла с разными форматами и колонками, разделенными табуляцией. Мне нужно сравнить столбцы column1 и column2 из file1 с file2. Если они совпадают, мне нужно заменить значение в column6 file1 на значение в column3 file2. Я пробовал использовать awk, но не смог заменить значение. Можете ли вы дать совет по следующему коду?

awk 'FILENAME == ARGV[1] {
    m[$1,$2] = $6;
    next;
}
{
    if (($1,$2) in m) {
        m[$6]= $3; print m[$6];
    }
}' file1 file2


первые строки файла1
1201 12011 1 0 0 0 1
1202 12021 1 0 0 0 1
1203 12031 1 0 0 0 1
1204 12041 1 0 0 0 2
1207 12071 1 0 0 0 2
1209 12091 1 0 0 0 1
1210 12101 1 0 0 0 1
1212 12121 1 0 0 0 1
1213 12131 1 0 0 0 1
1214 12141 1 0 0 0 2

первые строки файла2
1201    12011   1
1202    12021   1
1203    12031   1
1204    12041   1
1206    NA  1
1207    12071   2
1208    NA  1
1209    12091   2
1210    12101   2

Я хочу присвоить значения из файла2 в файл1, так как я хотел бы записать обновленное содержимое в другой файл out.txt

редактировать

Попробовал следующий код согласно комментариям

awk '{
    if (FNR==NR) {
        a[FNR]=$1;b[FNR]=$2;c[FNR]=$3}
    else {             
        if (a[FNR] == $1 && b[FNR] ==$2) {
            $6=c[FNR]} else {$6=$6};
           print $0;
        }
    }' file2 file1

Получил следующий вывод

1201 12011 1 0 0 1 1
1202 12021 1 0 0 1 1
1203 12031 1 0 0 1 1
1204 12041 1 0 0 1 2
1207 12071 1 0 0 0 2
1209 12091 1 0 0 0 1
1210 12101 1 0 0 0 1
1212 12121 1 0 0 0 1
1213 12131 1 0 0 0 1
1214 12141 1 0 0 0 2
awk -F"\t" -v OFS="\t" '{
    if (FNR==NR) {
        a[FNR]=$1;b[FNR]=$2;c[FNR]=$3}
    else {
        if (a[FNR] == $1 && b[FNR] ==$2) {
            $6=c[FNR]} else {$6=$6};
            print $0
        }
    }' file2 file1

Я думаю, это выполнит задачу. Сначала он сохранит 1-й, 2-й и 3-й столбцы файла 2 в массивы a, b и c соответственно с индексом FNR, если FNR = NR (это верно только для строк в файле2). В противном случае (это верно только для строк в файле1) происходит сравнение значений в массивах a и b с $1 и $2. Если они совпадают, 6-е поле заменяется значением из массива c, в противном случае оно сохраняет свое значение и печатает строку с разделителем табуляция.

Альтернативный подход без использования awk

while read f1 f2 f3; do 
    sed -E -i "s/^($f1\s+$f2.*)([0-9]+)$/\1$f3/" file1;
done < file2 

Предполагает, что последнее число – это целое. Если это не так, то ([0-9])$ нужно будет изменить на подходящую группу захвата.

Обратите внимание, что это зависит от изменения файла1 на месте, поэтому работайте с копией.

Смотря на ваш скрипт awk, вы правильно поняли об обработке каждого входного файла по-разному (хотя в большинстве случаев проверка NR == FNR работает лучше и быстрее, потому что числовые сравнения быстрее строковых… что будет существенно, если в любом из файлов будет много тысяч входных строк).

Тем не менее, есть несколько проблем, которые мешают вашему скрипту делать то, что вам нужно.

  1. Вы считываете файлы в неправильном порядке – так как вы хотите вывести строки из файла1 с замененным 6-м столбцом на 3-й столбец файла2, вам нужно сначала прочитать файл2, чтобы построить массив m (с индексами $1,$2 из файла2 и значениями $3 из файла2).

  2. Вы изменяете m[$6] и затем печатаете его. В лучшем случае это напечатает только один столбец.

Попробуйте что-то подобное:

$ awk 'NR == FNR    { m[$1,$2] = $3; next }
       ($1,$2) in m { $6 = m[$1,$2] }
       1' file2 file1
1201 12011 1 0 0 1 1
1202 12021 1 0 0 1 1
1203 12031 1 0 0 1 1
1204 12041 1 0 0 1 2
1207 12071 1 0 0 2 2
1209 12091 1 0 0 2 1
1210 12101 1 0 0 2 1
1212 12121 1 0 0 0 1
1213 12131 1 0 0 0 1
1214 12141 1 0 0 0 2

Кроме того, замените NR == FNR в первой строке на ваш оригинальный FILENAME == ARGV[1].

Для каждой строки file2 (первый файл, который считывается – обратите внимание на порядок аргументов файлов в командной строке) она сохраняет $3 в массиве m и переходит к следующей входной строке. Столбцы $1 и $2 используются в качестве индекса для массива.

Затем, при считывании file1 (и последующих входных файлов, если они есть), если ($1,$2) является индексом в массиве, она заменяет $6 значением, хранящимся в массиве m.

Затем она печатает текущую входную строку, независимо от того, была ли она изменена или нет (шаблон 1 сам по себе является идиомой/сокращением awk, означающим “напечатать текущую входную строку” – “1” оценивается как истинное, и действие по умолчанию – “напечатать”… таким образом, по сути “если истина истинна, то напечатать”)

Примечание: вы не предоставили образец ожидаемого вывода, поэтому я не могу проверить, соответствует ли вывод тому, что вам нужно (обычно мне нравится сохранять образец вывода OP в файл и использовать diff или cmp, чтобы убедиться, что мой вывод совпадает). Вывод моего скрипта awk соответствует моей интерпретации того, что вы спросили в своем вопросе.

Ответ или решение

Для решения задачи сравнения и замены значений между двумя файлами мы воспользуемся инструментом awk. Рассмотрим более детально каждую часть этого подхода, чтобы обеспечить четкое понимание процесса.

Постановка задачи

Мы имеем два файла с данными, разделёнными табуляцией. Наша цель состоит в том, чтобы сравнить значения в первых двух колонках первого файла (file1) со значениями в первых двух колонках второго файла (file2). Когда нахождение совпадений будет установлено, необходимо заменить значение в шестой колонке file1 значением из третьей колонки file2. В конце процесса результат будет записан в отдельный выходной файл (out.txt).

Шаги решения

  1. Чтение данных из файлов: Сначала прочитаем данные из второго файла и сохраним их в массив для последующего доступа.
  2. Сравнение значений: Затем перейдём к ряду данных из первого файла и сравним значения с данными, уже сохранёнными в массиве.
  3. Замена и вывод: При нахождении совпадений, сделаем замену и выведем обновленные данные или оригинальные, если совпадений не будет.

Пример реализации через awk

Вот пример кода на awk, который реализует описанные шаги:

awk -F"\t" -v OFS="\t" '
    NR == FNR { m[$1, $2] = $3; next }        # Сохраняем данные из file2 в массив m
    ($1, $2) in m { $6 = m[$1, $2] }          # Замена значения в шестой колонке при совпадении
    1                                         # Печать текущей строки
' file2 file1 > out.txt                      # Перенаправляем вывод в файл out.txt

Пояснение кода:

  • -F"\t" устанавливает символ табуляции в качестве разделителя полей.
  • -v OFS="\t" устанавливает выходной разделитель на табуляцию, чтобы сохранить формат.
  • NR == FNR: Эта конструкция позволяет обрабатывать сначала первый файл (в данном случае file2).
  • m[$1, $2] = $3: Создаём ассоциативный массив, где ключами являются значения из первой и второй колонок, а значениями — значения из третьей колонки.
  • ($1, $2) in m: Проверяем, есть ли нахождение совпадений в массиве при чтении из второго файла.
  • $6 = m[$1, $2]: При совпадении заменяем значение в шестой колонке на соответствующее значение из массива.
  • 1: Это сокращение для print, которое выводит каждую строку (модифицированную или нет).

Дополнительные соображения

  • Убедитесь, что вы работаете с копиями файлов, чтобы избежать потери данных.
  • Использование этого подхода с awk является эффективным по времени, особенно при работе с большими файлами.
  • Если необходимо, вы можете дополнительно обрабатывать форматы файлов, добавляя функционал для обработки других разделителей или форматов данных.

Заключение

Данный подход позволяет легко сравнивать и заменять значения между двумя файлами с использованием мощного инструмента awk. С приведённым скриптом вы сможете успешно решить задачу, обновив данные в file1, основываясь на значения из file2.

Оцените материал
Добавить комментарий

Капча загружается...