我需要在大型 ascii 文本文件上运行搜索/替换功能。输入文件的简短摘录:
gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "MSTRG.2";
gene_id "MSTRG.3";
该MSTRG
字符串将替换为模板文件中存在的另一个 ID:
MSTRG.1 AT1G01030
MSTRG.2 AT1G01010
MSTRG.3 AT1G01035
一个简单的 while 循环迭代模板的每一行并进行替换:
while read bef aft
do
echo "Searching for $bef"
echo "Replacing with $aft"
sed "s/$bef/$aft/g" input > output
done < template
发生的情况是,MSTRG.2
后面的条目被正确替换,但MSTRG.1
保持不变。输出如下所示:
gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "AT1G01010";
gene_id "AT1G01035";
更新
这就是我最终所做的。
while read bef aft
do
sed -i "s/$bef/$aft/g" input
done < template
答案1
您的问题是,您在循环的每次迭代中都破坏了输出文件,只留下最近的更改,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
不识别-
为标准输入的含义。要将此方法与此类 a 一起使用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
作为正则表达式,意味着.
(点)将匹配任何特点。
答案2
您可以将输入文件复制为输出文件并对此输出文件进行操作,而不是在每次循环迭代中覆盖输出文件。
Withsed
的-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
答案3
#!/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
,但如果有很多行,则可以导致非常显着的加速。
on -i
the #!/usr/bin/perl -i
line 导致 perl 对输入文件进行就地编辑,而不是仅仅将更改打印到 stdout。例如,-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
语句,然后在成功搜索和替换后立即转到下一个输入行。
顺便说一句,请参阅为什么使用 shell 循环处理文本被认为是不好的做法?为什么用 sh 循环进行文本处理不是一个好主意。使用awk
orperl
或sed
或其他任何内容代替sh
or bash
。