【问题标题】:Partial string match using AWK使用 AWK 进行部分字符串匹配
【发布时间】:2020-10-16 14:06:40
【问题描述】:

大家早上好!

我在 file.txt 中有以下几行:

big bird|Big birds tend to be big.;;They have wings.
big truck|I love big trucks!;;Ford makes nice big trucks. 
red truck|Red is my favorite color.;;Red is also a name.

我的两个分隔符是竖线 (|) 和双分号 (;;)。我的脚本接受一个输入,将其与管道 (|) 之前的 file.txt 中的一行匹配,并在由 (;;) 分隔的管道之后随机返回相应的输出。

read -p " " query
response="$(awk -F\| -v r="$query" '$1==r{print $2;exit}' file.txt | sed 's/;;/\n/g' | shuf -n1)"
echo "$response"

例子:

Input: big truck
Output: Ford makes nice big trucks.

但这不起作用,除非输入完全匹配。如何修改 awk 表达式以允许它接受部分匹配或在两个单词倒置的情况下? (例如“大卡车”而不是“大卡车”

匹配“大”和“卡车”并从线路返回随机输出的期望行为:

Input: some trucks are big
Output: Ford makes nice big trucks.

提前非常感谢!

【问题讨论】:

  • 对于部分匹配,您可以使用$1 ~ r(但是您必须小心\ 字符)...匹配单词的不同组合将取决于您是否需要通用解决方案或者是否你有固定数量的术语,例如,如果它总是由空格分隔的一两个单词,或者可能有类似 big green australian sub-urban mammal 或其他的东西
  • 当输入为big bird truck,甚至big red bird truck 时会发生什么?
  • @Sundeep 5 或 6 个字将是它的范围。输入不会比这大得多。您能否详细说明如何在上面的代码库中使用 $1 ~ r 以及您提到的 \ 字符?为了安全起见,是否需要逃脱某些东西?如何逃脱?谢谢。
  • @Luuk 这种情况即使不是永远也不会发生。在这种情况下,它应该返回第一个匹配的行。非常感谢你提出来。很重要的一点。

标签: regex linux bash awk


【解决方案1】:

不清楚您是否需要正则表达式或字符串匹配,也不清楚您是否想要对整个 $1 或 $1 中的“单词”的一部分或 $1 中的整个单词或其他内容进行部分匹配。以下将对 $1 的部分进行全字字符串匹配,因为在我看来这是您最有可能要求的。您也没有说明您希望如何处理输入或查询字符串中的重复项,以便以下匹配唯一单词(例如,与计算单词的出现次数相反):

$ cat tst.sh
#!/usr/bin/env bash

read -p " " query

response="$(
    awk -v query="$query" '
        BEGIN {
            split(query,tmp)
            for (i in tmp) {
                targets[tmp[i]]
            }

            for (word in targets) {
                targetCnt++
            }

            FS = "[|]"
        }
        {
            delete present
            split($1,tmp," ")
            for (i in tmp) {
                present[tmp[i]]
            }

            matchCnt = 0
            for (word in targets) {
                if (word in present) {
                    matchCnt++
                }
            }

            if ( targetCnt == matchCnt ) {
                sub(/.*;;/,"")
                print
            }
        }
    ' file |
    shuf |
    head -1
)"

printf '%s\n' "$response"

.

$ ./tst.sh
 truck
Red is also a name.
$ ./tst.sh
 truck
Ford makes nice big trucks.
$ ./tst.sh
 truck big
Ford makes nice big trucks.
$ ./tst.sh
 truck big
Ford makes nice big trucks.

【讨论】:

    【解决方案2】:

    脚本'do.awk':

    BEGIN{
            split(input,s," ");
            for (i in s) s2[s[i]]=i;
            srand();
    }
    {
            split($1,a," ");
            m=0;
            for(i in a) {
                    if (a[i] in s2) then m++;
            }
            # add if words match (m>0), first matchcount, then $2
            if (m>0) {
                    r[z++]=m";;"$2
                    }
    }
    END {
            # sort array r, last line will have highest matchcount
            n = asort(r);
            # print last value
            # print r[n];
            # get random piece, but exclude r2[1], because it is matchcount.
            x=split(r[n],r2,";;")-1;
            x = int(rand()*x);
            print  r2[x+2];
    }
    

    awk -v input="big red" -F\| -f do.awk file.txt 将输出“红色也是一个名字”或“红色是我最喜欢的颜色”

    我用来自 awk 的函数 rand() 替换了您使用 shuf 的随机函数。 (我希望它足够随机)。

    编辑: 我注意到你在这里有另一篇关于 RANDOM 的帖子:BASH - Regex match line in file.txt with more than one delimiter

    编辑2: 多行的匹配计数可以相同。如果这是真的,我们应该添加所有文本,然后随机选择一个。

    我将END部分更改如下:

    END {
            # sort array r, last line(s) will have highest matchcount
            n = asort(r);
            split(r[n],t,";;");
            m = t[1];
            # add all textx with matchcount=m
            o="";
            for(i=length(r); i>=1; i--) {
                    split(r[i],t,";;")
                    if(t[1]==m) {
                            for (j=2; j<=length(t); j++) {
                                    o=o";;"t[j];
                            }
                    } else {
                            break;
                    }
            }
    
            # get random piece, from o.
            x=split(o,r2,";;")-1;
            m=r2[1];
            x = int(rand()*x);
            print  r2[x+2];
    }
    

    编辑: 最后我应该提到asort() 仅在 gawk 中可用。

    【讨论】:

    • 非常感谢@Luuk,非常酷的方法。它工作得很好,除了它似乎在随机返回任何匹配时,它应该返回“最佳匹配”本身。例如,当我从 file.txt 中的“大鸟”行中删除小鸟并有类似“大|大是形容词。;;大不是小。”时,input=big 从带有卡车的行返回这一行的 100% 匹配,没有剩余的关键字。有没有机会我可以让你解决这个问题以表示衷心的感谢?
    • “matchcount”在多于 1 行中是相同的,目前它从最后一行返回一个随机片段。我会看看如果我可以添加这个。
    • 嗨@Luuk,感谢您的出色努力,但它仍然没有返回“最佳匹配”。当存在 100% 匹配且没有剩余输入关键字时,应优先考虑并返回该关键字。例如,当输入只是“大”时,它应该从这一行返回:“大|大是一个形容词。;;大不是小。”,而不是大卡车|我喜欢大卡车!;;福特很好big trucks..... 因为在第一种情况下,它是 100% 的单词匹配(只有“big”),而在第二种情况下,它是 50% 的单词匹配(“big”和“truck”)。
    • 我给了你一个开始的东西,你可以自己调整这些东西。 SO是关于学习的,你也应该学习一些东西?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2013-06-04
    • 2018-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    相关资源
    最近更新 更多