我有两个文件:
Ref.txt 其中包含:
ABCDEFG,15147,ABC,ABCD,ABCDE
HIJKLMN,500,HIJKLM,HIJKL,HIJK
和 Seq.txt 其中包含:
ABCDEFG
ABCXXXX
ABCDXXX
ABCDEXX
HIJKXXX
HIJKLXX
HIJKLMX
HIJKLMN
我想在 Seq.txt 文件中搜索 Ref.txt 文件每行的 3-end 字段,并将找到该字符串的行替换为该行第一个字段中的字符串。我在这个例子中寻找的输出是这样的:
ABCDEFG
ABCDEFG
ABCDEFG
ABCDEFG
HIJKLMN
HIJKLMN
HIJKLMN
HIJKLMN
虽然这是不正确的代码,但我正在考虑这样的命令:
awk '{for(i=3; i<=NF; i++) gsub( $i , $1)}
但其中 for 循环引用 Ref.txt 文件,并且 gsub 命令在 Seq.txt 文件上执行。
答案1
您可能考虑这样做的一种方法是,通过将它们与 链接起来,从 Ref.txt 中每行的第 3 端字段构建单个正则表达式|
,并将其保存在以值为键的关联数组中$1
。
然后,您可以循环遍历每个 Seq.txt 的键$1
:
awk -F, '
NR==FNR {
# construct a single ERE as $3|$4|$5|etc. keyed on $1
for(i=3;i<=NF;i++) r[$1] = r[$1] == "" ? $i : r[$1] "|" $i;
next
}
{
# test $1 against each ERE and substitute the first matching key
for(k in r) {
if ($1 ~ r[k]) {
$1 = k;
break
}
}
}
1
' Ref.txt Seq.txt
答案2
您可以sed
仅使用Posix
如下所示的构造来实现此目的:
$ sed -ne '
/,/!G
y/\n_/_\n/
s/^\([^_][^_]*\).*__\(.*_\)\{0,1\}\([^,]*\),[^,]*,[^_]*,\1,.*/\3/p
s/^[^_]*__//;s/$/,/
y/_\n/\n_/
/\n/!H
' Ref.txt Seq.txt
注意:这是在bash
命令行上运行的,参数的顺序和数量如此处给出。
Perl
也可以在此处使用,而无需借助正则表达式:
$ perl -F, -lane '
if ( @ARGV ) {
$h{$_} = $F[0] for @F[2..$#F];
next;
}
my $seq = $_;
my($k) = grep { ! index($seq, $_) } keys %h;
print $h{$k};
' Ref.txt Seq.txt
答案3
如果您像我一样懒,您不想处理数组和循环并将其留给您的工具。这就是为什么我sed
更喜欢将这些Ref.txt
行(由逗号标识)放在保留空间上H
。实际上我添加了另一个逗号,所以我知道模式总是位于两个逗号之间。所以加起来就是/,/{s/$/,/;H;d;}
.
现在,对于 的每一行Seq.txt
,我将参考堆附加到保留空间,G
并让s
命令用在堆中找到的模式替换该模式。通常,正则表达式编写起来比读取起来容易。
现在让我们看看模式空间:
ABCDEFG\n\nABCDEFG,15147,ABC,ABCD,ABCDE\nHIJKLMN,500,HIJKLM,HIJKL,HIJK
\_____/ \_____/ \_/
replace by this if match
- 要替换的部分位于开头 (
^
) 和匹配部分 ([A-Z]+
) 以及前后一些部分 ([A-Z]*
) - 替换是换行符和逗号之间的序列:
\n[A-Z]*,
- 匹配是两个逗号之间的模式,从第一部分反向引用:
,\1,
总而言之,这给出了
sed -E '/,/{s/$/,/;H;d;};G;s/^[A-Z]*([A-Z]{1,})[A-Z]*\n.*\n([A-Z]*),[A-Z0-9,]*,\1,.*/\2/;P;d' Ref.txt Seq.txt