【问题标题】:find all values from text a in text b and type next to it other column from other file with awk在文本 b 中查找文本 a 中的所有值,并在其旁边使用 awk 键入来自其他文件的其他列
【发布时间】:2013-12-15 19:39:39
【问题描述】:

一个.txt

xa,xono a,y,z,abc
xb,u,v,def
xc,w,x,ghi
xd,a yuli x,c,u,jkl
xe,u,v,w,x,y,z,mno
xf,z,u,p dao,pqr

b.txt

1,a
2,b
3,c
4,d
.....(etc)
21,u
22,v
23,w
24,x
25,y
26,z
27,xono a
28,a yuli x
29,p dao
30,...
31,abc
32,def
33,ghi
34,jkl
35,mno
36,pqr

现在 b.txt 中的第二列值,我想查找它们是否存在于 a.txt 中,如果存在,我们将在 b.txt(或其他文件)的第二列旁边写入 a 中的第一列.txt 来获取这样的文件:

1,a
2,b
3,c,xd
4,d
.....(etc)
16,p
.....(etc)
21,u,xb,xd,xe,xf
22,v,xb,xe
23,w,xc,xe
24,x,xc
25,y,xa,xe
26,z,xa,xe,xf
27,xono a,xa
28,a yuli x,xd
29,p dao,xf
30,...
31,abc,xa
32,def,xb
33,ghi,xc
34,jkl,xd
35,mno,xe
36,pqr,xf

【问题讨论】:

  • 看看man join

标签: bash shell sed awk statistics


【解决方案1】:

BASH/awk 解决方案:

IFS=, && while read -r p q; do awk -v p="$p" -v q=",$q" -F, 'BEGIN{
     printf "%s%s", p, q; gsub(/\./, "\\.", q); r=q"(,|$)"}
     $0 ~ r{printf ",%s", $1; next} END {print ""}' a.txt; done < b.txt
1,a
2,b
3,c,xd
4,d
.....(etc),
21,u,xb,xd,xe,xf
22,v,xb,xe
23,w,xc,xe
24,x,xc,xe
25,y,xa,xe
26,z,xa,xe,xf
27,xono a,xa
28,a yuli x,xd
29,p dao,xf
30,...
31,abc,xa
32,def,xb
33,ghi,xc
34,jkl,xd
35,mno,xe
36,pqr,xf

【讨论】:

  • 当然。 while 循环使用IFS=,b.txt 中的每个值用于shell 变量p and q。然后p and q 作为参数传递给 awk。然后,Awk 使用,a,b 等从a.txt 搜索每一行,如果找到这些值就会打印出来。 print BEGIN/END 中的语句用于打印参数和新行。
  • printf in BEGIN 用于打印"1,a", "2,b", "26,z" 等,第二个 printf 用于打印",v" ",xb" ",xe" 等。而awk -v p="$p" -v q=",$q" 是将shell 变量传递给awk 的awk 方式。
  • 是的 :D 你是个好人!非常感谢 !那么你添加了什么 ~r ?是为了什么?
  • $0 ~ r 实际上是正则表达式匹配,因为我将var r 创建为,p(,|$),这意味着,p 后跟逗号或行尾。
  • 是的,我已经看到了该解决方案,但由于它需要 awk 4+ 版本,我无法在本地测试它,因为我的 awk 在 OSX 上并且不是 4+
【解决方案2】:

我知道您要的是awk,是的,这是可能的。也许您可以根据perl 中的此实现自己编写:

#!/usr/bin/perl

use strict;

my %mapping;

open(my $a, 'a.txt');
while (<$a>) {
    chomp;
    my ($xx, @letters) = split(/,/);
    foreach my $letter (@letters) {
        if (!exists($mapping{$letter})) {
            $mapping{$letter} = [];
        }
        push(@{$mapping{$letter}}, $xx);
    }
}

open(my $b, 'b.txt');
while (<$b>) {
    chomp;
    my ($num, $letter) = split(/,/);
    if (exists($mapping{$letter})) {
        print join(',', $_, @{$mapping{$letter}}), "\n";
    } else {
        print $_, "\n";
    }
}

【讨论】:

    【解决方案3】:

    gawk 4.1 版中,您可以使用数组的数组。试试

    gawk -F, -f s.awk a.txt b.txt 
    

    s.awk 在哪里:

    NR==FNR {
        for (i=2; i<=NF; i++)
            a[$i][$1]++
        next
    }
    
    {
        keys=""
        if ($2 in a) {
            for (i in a[$2])
                keys= keys","i
        }
        print $0 keys
    }
    

    解释:

    • 我们使用gawk 第 4 版中引入的一项新功能。称为“数组的数组”,请参阅http://www.gnu.org/software/gawk/manual/gawk.html#Arrays-of-Arrays
    • 来自a.txt 的键首先存储在数组a 中。例如,a["xono a"]["xa"] 是数组中的一个元素。
    • 对于b.txt,我们只需检查$2 是否在a 内。如果是这样,我们将生成属于该值的一组键。
    • print $0 keys$0keys 的字符串连接,即print ($0 keys)

    【讨论】:

    • 我喜欢这个解决方案,但它与重复字符串 u,v,w,x,y,z 显示的内容不匹配:21,uxd 22,vxb 23,wxc 24,xxc 25,yxa 26,zxa 它应该显示:21,u,xb,xd,xe,xf 22,v,xb,xe 23,w,xc,xe 24,x,xc 25,y,xa,xe 26, z,xa,xe,xf
    • 清晰、简单、令人敬畏!谢谢你 !键的解释和打印 $0 键?每次找到正确的字符串时,我们都会连接该值?
    • 我已经用 awk(不是 gawk)尝试过它,它也可以工作......这正常吗?最后一个问题:-F -f 是为了什么??
    • @user3057111 我确信我已经安装了 Gnu Awk 3.1.8 版和 4.1.0 版。它不适用于 3.1 版。如果你输入awk --version,你会得到什么?
    • @user3057111 不是-F -f。首先是-F,,将字段分隔符设置为,,然后-f s.awk 告诉awk 从文件s.awk 中读取脚本。
    【解决方案4】:

    这可能对你有用(GNU sed):

    sed -r 's|^([^,]+)(,[^,]+)|/^[^,]+\2,?/s/$/,\1/\n\1|;/,/P;D' a.txt|sed -rf - b.txt
    

    这会从 a.txt 构建一个 sed 脚本,该脚本针对 b.txt 运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-22
      • 1970-01-01
      • 2016-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多