【问题标题】:awk match() multiple matchesawk match() 多个匹配
【发布时间】:2017-03-26 22:49:17
【问题描述】:

我有以下几点:

echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|awk '{match($0,/ZZ:Z[^ ]*/,m); print m[0], m[1]}' 

不幸的是,它只输出第一个条目(两个中的):

ZZ:Z:mus.sup 

在我看来,match() 函数无法将多个匹配项存储到其数组中。除非我在这里遗漏了什么……?

如果确实如此,有人会建议一个基于 awk 的“匹配”替代方案,该替代方案将允许获得两个 ZZ:Z 条目。请注意,这些不是每次都位于同一列(!) - 因此需要使用 match() 函数。

这里的总体思路是在同一个 awk 命令中获取一些出现在已知列位置的值(例如 col1、col2),以及一些位于未知列的值(根据它们的唯一签名“ZZ:Z”获取)索引列。

此外,以下尝试 - 使用 gensub() 也无法输出/打印两个 ZZ:Z 条目,并且仅识别两个条目中的一个(以及另一个在弃用倒数时..)

echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|awk '{val= gensub(/.*(ZZ:Z[^ ]*).*/,"\\1 \\2","g",$0);print val}'

这种情况下的结果是:

ZZ:Z:cas.sup

但我想得到这样的结果:

ZZ:Z:mus.sup ZZ:Z:cas.sup 

【问题讨论】:

    标签: awk match


    【解决方案1】:

    match 的结果可用于获取不匹配的部分 附加匹配:

    {
            for (s = $0; match(s, /ZZ:Z[^ ]*/);
                s = substr(s, RSTART + RLENGTH, length))
                    printf("%s%s", s == $0 ? "" : " ", 
                        substr(s, RSTART, RLENGTH))
            print ""
    }
    

    或者,可以根据唯一标识符拆分字符串, 使用splitFS

    {
            l = split($0, a, /ZZ:Z/)
            for(i = 2; i <= l; i++)
                    printf("%s%s", i == 2 ? "" : " ",
                        "ZZ:Z" substr(a[i], 1, index(a[i], " ") - 1))
            print ""
    }
    

    【讨论】:

    • 这是一个简洁的通用解决方案,可以使用尽可能多的外观 - 非常好! tnx
    【解决方案2】:

    谢谢,上述解决方案非常棒,并为问题提供了通用解决方案 - 无论 ZZ:Z 条目在原始行中重复多少次。

    然而,这是我的目标,这是对我上面使用的错误匹配条件的修复:

    echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|awk '{val= gensub(/.*(ZZ:Z[^ ]*).*(ZZ:Z[^ ]*).*/,"\\1 \\2","g");print val}'
    

    输出:

    ZZ:Z:mus.sup ZZ:Z:cas.sup
    

    另外,这是解决方案 - 使用 awk 的 match() :

    echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|awk '{match($0,/.*(ZZ:Z[^ ]*).*(ZZ:Z[^ ]*).*/,m); print m[1], m[2]}'
    

    【讨论】:

    【解决方案3】:

    你只是调用了错误的函数,你应该使用split()而不是match()

    $ echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|
    awk '{split($0,t,/ZZ:Z[^ ]*/,m); print m[1], m[2]}'
    ZZ:Z:mus.sup ZZ:Z:cas.sup
    

    或者按照它们在输入中出现的顺序打印任意数量的出现:

    $ echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|
    awk '{split($0,t,/ZZ:Z[^ ]*/,m); for (i=1; i in m; i++) print m[i]}'
    ZZ:Z:mus.sup
    ZZ:Z:cas.sup
    

    这使用 GNU awk 作为第 4 个参数来 split(),就像你使用 GNU awk 作为第 3 个参数来匹配()。

    如果您必须在非 GNU awk 中执行此操作,那就是:

    $ echo AS:i:0  UQ:i:0  ZZ:Z:mus.sup  NM:i:0  MD:Z:50  ZZ:Z:cas.sup  CO:Z:endOfLine|
    awk '{while(match($0,/ZZ:Z[^ ]*/)) {print substr($0,RSTART,RLENGTH); $0=substr($0,RSTART+RLENGTH)}}'
    ZZ:Z:mus.sup
    ZZ:Z:cas.sup
    

    【讨论】:

    • 能否请您检查一下您的解决方案,这可能只是我的问题,但我确实收到一条错误消息:“ awk: fatal: 4 is invalid as number of arguments for split ”跨度>
    • 您需要使用 GNU awk 4.0 或更新版本。如果您使用的是旧版本,则需要尽快更新,因为 4.0 已经存在 5 年以上(4.0.0 于 2011 年 6 月发布,我们现在使用的是 4.1.4 版!),而您缺少一个大量非常有用的功能和错误修复(请参阅gnu.org/software/gawk/manual/gawk.html#Feature-History
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-12
    • 2018-02-22
    相关资源
    最近更新 更多