Вместо того, чтобы перезаписывать выходной файл в каждой итерации цикла, вы можете копировать входной файл как выходной файл и работать с этим выходным файлом.
С параметром sed
и -i
изменения записываются в место -того же файла, поэтому предыдущие замены не теряются:
cp input output
while read bef aft
do
echo "Searching for $bef"
echo "Replacing with $aft"
sed -i "s/$bef/$aft/g" output
done < template
Ваша проблема заключается в том, что вы затираете выходной файл на каждой итерации цикла, оставляя только самые последние изменения в output
и ни одно из более ранних.
Вместо этого вы можете легко преобразовать файл template
в набор команд sed
:
$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template
s/MSTRG.1/AT1G01030/g
s/MSTRG.2/AT1G01010/g
s/MSTRG.3/AT1G01035/g
... а затем примените их к своему файлу:
$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template | sed -f - input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
Некоторые реализации sed
не распознают -
как стандартный ввод. Чтобы использовать этот подход с таким sed
, замените -f -
на -f /dev/stdin
.
Или вы можете просто сделать все это вawk
:
$ awk 'FNR == NR { pat[$1] = $2; next } { for (p in pat) gsub(p, pat[p]); print }' template input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
Обратите внимание, что во всех приведенных выше вариантах используется то, что находится в первом столбце template
в качестве регулярного выражения , что означает, что.
(точка )будет соответствовать любому персонаж.
#!/usr/bin/perl -i
use strict;
# The %re hash holds the regexp searches and replacement strings.
my %re = ();
my $tfile = shift;
open(TEMPLATE, "<", $tfile) || die "couldn't open $tfile for read: $!\n";
while(<TEMPLATE>) {
chomp;
my ($search,$replace) = split;
$re{qr/$search/} = $replace;
};
close(TEMPLATE);
while (<>) {
foreach my $s (keys %re) {
s/$s/$re{$s}/g;
};
print;
}
Это читает файл template
и создает ассоциативный массив (, также известный как "хеш" ), называемый %re
поиском и заменой регулярных выражений.
Затем он перебирает все оставшиеся имена файлов в командной строке (, например.input
)и выполняет все эти операции поиска и замены для каждой строки ввода. Он использует qr//
для предварительной -компиляции регулярных выражений -это всего лишь тривиальная оптимизация, если в template
не так много строк, но может привести к очень значительному ускорению, если строк много.
-i
в строке #!/usr/bin/perl -i
заставляет Perl вносить изменения в -поместить во входные файлы, а не просто выводить изменения на стандартный вывод. Измените это, например, на -i.bak
, если вы хотите, чтобы сохранялась резервная копия файлов до того, как они были изменены.
Сохранить как, например, cryptic0.pl
, сделать его исполняемым с помощью chmod +x cryptic0.pl
и запустить так:
$./cryptic0.pl template input
Сценарий не будет выводить данные на терминал. Вместо этого он отредактирует входной файл (с ).
Например, ваш файл input
будет изменен на:
$ cat input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
Кстати, этот сценарий изменит все совпадения во всех строках на соответствующую строку замены. Если вы уверены, что в любой данной строке может быть только одно совпадение, вы можете ускорить его, изменив эту строку:
s/$s/$re{$s}/g;
к этому:
s/$s/$re{$s}/ && last;
Это приводит к тому, что сценарий переходит из цикла foreach к оператору print
, а затем переходит к следующей строке ввода, как только он успешно выполнил поиск и замену.
Кстати, см. Почему использование цикла оболочки для обработки текста считается плохой практикой? почему не рекомендуется выполнять обработку текста с помощью циклов sh. Используйте awk
или perl
или sed
или что-то другое вместо sh
или bash
.