使用 AWK for 循环作为查找和替换的输入

使用 AWK for 循环作为查找和替换的输入

我有两个文件:

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

相关内容