замена имен с использованием информации в других переменных

#linux #bash

#linux #bash

Вопрос:

У меня есть файл, содержащий список объектов в первом столбце и другие 3 столбца с 3 разными, но взаимосвязанными переменными. Файлы выглядят так:

 file.txt:

aaaa_111______ GCP 1 yes
bbbb_23e______ DFR 1 no
cccc_345______ TRE 4 no
dddd_65e______ WER 2 yes
eeee_456______ YTR 1 no
ffff_222______ HYT 4 yes
gggg_345______ UIY 2 no
 

Мне нужно получить новый файл, в котором я изменяю второй столбец (имя лиганда), сопоставляя третий и четвертый столбцы. Для двух объектов, если число в третьем столбце одинаковое, я хочу заменить имя во втором столбце, используя имя объектов, которые в четвертом столбце есть «да».

Конечный файл должен быть таким:

 aaaa_111______ GCP 
bbbb_23e______ GCP
cccc_345______ HYT
dddd_65e______ WER 
eeee_456______ GCP
ffff_222______ HYT 
gggg_345______ WER
 

Я заменил имена лигандов (второй столбец), для которых в четвертом столбце было написано «нет», на имя лиганда, который в третьем столбце имел тот же номер, а в четвертом было написано «да».
Надеюсь, я выразился ясно, но я понимаю, что проблема достаточно сложна, чтобы объяснить ее словами. Может кто-нибудь подсказать, как это сделать? Спасибо!

Ответ №1:

Perl на помощь!

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

 #!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

open my $in, '<', shift or die $!;

my %replace;
while (<$in>) {
    my ($ligand, $number, $keep) = (split)[1, 2, 3];
    if ($keep eq 'yes') {
        die "Duplicate $number $ligand"
            if exists $replace{$number};

        $replace{$number} = $ligand;
    }
}

seek $in, 0, 0;
while (<$in>) {
    my ($obj, $ligand, $number) = (split)[0, 1, 2];
    if (exists $replace{$number}) {
        say join ' ', $obj, $replace{$number};
    } else {
        warn "Can't replace: $_";
    }
}
 

Комментарии:

1. Спасибо. Теперь я пытаюсь, но куда мне вставить исходный файл, из которого можно прочитать информацию? Извините, но я непрактичен.

2. @TommasoPalomba: это должно быть задано в качестве параметра скрипта. perl this-script input-file

3. script_replace.sh : строка 2: использовать: команда не найдена script_replace.sh : строка 3: использовать: команда не найдена script_replace.sh : строка 4: использование: команда не найдена Impossibile ottenere un descritore di file che si riferisce alla консоль script_replace.sh : строка 8: my: команда не найдена script_replace.sh : строка 9: синтаксическая ошибка, близкая к неожиданнойтокен )' script_replace.sh: line 9: while (<$in>) {‘

4. Я получил это сообщение

5. @TommasoPalomba Это сценарий perl, а не сценарий оболочки…

Ответ №2:

 awk 'NR==FNR { if($4=="yes") { map[$3]=$2} } NR!=FNR { if ($4=="no") { $2=map[$3] } print $1" "$2 }' file.txt file.txt
 

Используя awk, обработайте файл дважды. На первом проходе (NR ==FNR) создайте массив, если 4-е поле, разделенное пробелом, равно yes. Индекс является третьим разделителем, а значение — вторым. Затем на втором проходе (NR!=FNR), когда 4-е поле равно no, замените 2-е поле значением в массиве map и выведите необходимые данные. В противном случае просто выведите необходимые данные.

Комментарии:

1. Это не работает. Это дает тот же результат, что и исходные файлы без столбцов 3 и 4, но имя не заменяется

2. ОК. Какую версию awk вы используете? (awk -V)

3. Кроме того, являются ли разделители полей фактически пробелами, как в примере?

4. Версия: GNU Awk 4.0.2, а разделителем между столбцами является вкладка

5. Та же версия, что и у меня, и поэтому она должна работать. Это решение просто выводит на экран, а не записывает в файл.