匹配两个文件中的字符串并按特定顺序输出字段

时间:2019-03-23 16:36:31

标签: bash awk

我有以下两个文件。

query.txt

pumpkin
kiwi

subject.tsv

kiwifruit   something   green
melon   something   red
pumpkinhead something   orange

我想遍历query.txt并检查每一行,如果subject.tsv中的任何第一个字段都包含该行。如果是,则将选项卡以及subject.tsv中的第三个字段添加到该行。 query.txt中各行的顺序应保留在输出中。

我尝试了以下方法。

while read query; do echo -e $query"\t"; awk '$1 ~ "$query" {print $3}' subject.tsv; done < query.txt

所需的输出:

pumpkin orange
kiwi    green

实际输出:

pumpkin
kiwi

5 个答案:

答案 0 :(得分:2)

Bash不会对单引号(')中公开的字符串执行变量扩展,因此脚本的问题在这里:

'$1 ~ "$query" {print $3}'

一定是这样的:

'$1 ~ '"$query"' {print $3}'

但是,即使在修复该脚本后,它的效率仍然很低并且容易出错。因此,我建议您在单个awk调用中执行以下操作:

awk -F'\t' -v OFS='\t' '
NR==FNR { a[$0] = $3; next } {
    for (b in a) {
        if (index(b, $0)) {
            print $0, a[b]
            break
        }
    }
}' subject.tsv query.txt

答案 1 :(得分:1)

尽管问题中缺少很多信息,例如查询文件是否包含唯一字符串?还是主题文件包含重复的字符串?但鉴于当前文件和要求,请在下面给出答案,由于它是基于awk的,因此它也应适用于大型文件:

awk ' BEGIN{OFS="\t"}
    FILENAME ~ /subject.tsv$/ {
      color[$1]=$3
    }
    FILENAME ~ /query.txt$/ {
      for (i in color) {
      if ( i ~ $1 ) print $1, color[i]
    }
 }'  subject.tsv query.txt

下面是输出:

    pumpkin orange
    kiwi    green

答案 2 :(得分:0)

在使用while的代码版本中,您需要使用awk选项将要搜索的字符串传递给-v命令,否则awk将处理所有作为纯字符串。

例如,如果要传递变量query包含的字符串'pumpkin',则需要这样做:

query="pumpkin"; awk -v query=$query '$1 ~ query' subject.tsv

-v用于声明一个变量,该变量使用awk之外的变量的内容。您可以看到awk

的说明
  

-v var = val
  --assign var = val
  
  在程序开始执行之前,将值val分配给变量var。这样的变量值可用于AWK程序的BEGIN规则。

因此,您可以尝试通过将query传递到awk来相应地修改代码。

答案 3 :(得分:0)

这里是bash解决方案

while read pattern; do
    column1=$(cut -d " " -f1 subject.tsv | grep "$pattern")
    allcolumns=$(echo "$pattern $(grep $column1 subject.tsv)")
    echo $allcolumns | cut -d " " -f1,4
done < query.txt

说明

阅读query.txt的所有行

while read pattern; do
   ...
done < query.txt

在第1列中找到搜索模式

while read pattern; do
   column1=$(cut -d " " -f1 subject.tsv | grep "$pattern") 
   echo $column1
done < query.txt

输出

pumpkinhead
kiwifruit

将query.txt中的模式与subject.tsv中的匹配项合并

while read pattern; do
    column1=$(cut -d " " -f1 subject.tsv | grep "$pattern")
    allcolumns=$(echo "$pattern $(grep $column1 subject.tsv)")
    echo $allcolumns
done < query.txt

输出

pumpkin pumpkinhead something orange
kiwi kiwifruit something green

首先从合并中提取最后一个字段

echo $allcolumns | cut -d " " -f1,4

输出

pumpkin orange
kiwi green

答案 4 :(得分:0)

您可以尝试使用此命令行Perl

$ perl -lane ' BEGIN { open($fh,"query.txt");@q=<$fh>;chomp(@q) } @s=/(\S+)/g; 
    /$q[0]/ and print "$q[0] $s[2]"; if(eof) { close($ARGV); shift @q}; ' subject.tsv subject.tsv

使用您的输入。

$ perl -lane ' BEGIN { open($fh,"query.txt");@q=<$fh>;chomp(@q) } @s=/(\S+)/g; 
     /$q[0]/ and print "$q[0] $s[2]"; if(eof) { close($ARGV); shift @q}; ' subject.tsv subject.tsv

pumpkin orange
kiwi green

$