【问题标题】:GAWK concat variables in FORFOR中的GAWK concat变量
【发布时间】:2016-11-16 06:54:41
【问题描述】:

我当前的 GAWK 脚本获取短语文件,并创建正则表达式模式数组,然后用 \t 字符分割每一行并循环每行的前 10 列,然后检查它是否包含模式数组中的至少一个短语,如果确实如此,它会跳过该行并且不将其打印到文档中。

问题:

因为短语文件很大,它会产生大量的迭代并使脚本变得非常慢。

(700 个模式 x 10 列(由制表符 \t 分隔))x 1000 行。

解决方案:

为了提高速度,我想连接前 10 列,并检查整个字符串是否包含至少一个模式。我不知道如何在 FOR 循环中连接行。

工作示例:

gawk 'BEGIN{
FS=" *\t *";
IGNORECASE=1;

while(getline a < "'$phpath'") PATS["^.*"a".*$"]
}

{
    ok=1;
    for(i=1;i<=10;i++){
        for(p in PATS){
            if($i ~ p){
            ok=0
            }
        }
    }

} 
ok {print}' "$f" > "$newPath$filename" 

我的尝试:

gawk 'BEGIN{
    FS=" *\t *";
    IGNORECASE=1;

    while(getline a < "'$phpath'") PATS["^.*"a".*$"]
    }

    {
        phrase="";
        space=" ";
        ok=1;

        for(i=1;i<=10;i++){
            phrase = $space $phrase $i
        }

        for(p in PATS){
            if($phrase ~ p){
                ok=0
            }
        }

    } ok {print}' "$f" > "$newPath$filename"

【问题讨论】:

    标签: bash awk gnu gawk


    【解决方案1】:

    在 awk 中,您使用 $ 就像 解引用运算符,其中 $x 的意思是“给我在变量 x 中编号的列的值”

    将前 10 列变成一个字符串:

        for (i=1; i<=10; i++) {
            # not this => phrase = $space $phrase $i
            phrase = space phrase $i
        }
    

        for (p in PATS) {
            if (phrase ~ p) {   # <= no $
                ok = 0
                break           # no match, so break the loop early
            }
        }
    

    awk 使用类似于 C 的变量,而不是 shell 或 perl


    你也可以试试这个:

    gawk -v patternfile="$phpath" '
        BEGIN {
            FS = " *\t *"
            IGNORECASE = 1
            while ((getline a < patternfile) > 0)
                PATS["^.*"a".*$"]
        }
        {
            line = $0
            NF = 10         # this truncates the current record to 10 fields
            ok = 1
            for (p in PATS) 
                if ($0 ~ p) {
                    ok = 0
                    break
                }
            if (ok) 
                print line
        }
    ' "$f" > "$newPath$filename"
    

    【讨论】:

    • 感谢您提供非常清晰的解释和建议的解决方案。您的代码看起来更轻量级。在您建议的解决方案中循环模式时我需要休息一下吗?
    • 如果在读取模式文件时遇到错误,将进入无限循环,因为 getline 将返回 -1。你需要while ( (getline a &lt; patternfile) &gt; 0 ),见awk.freeshell.org/AllAboutGetline。您可以通过从模式文件创建| 分隔的字符串而不是填充数组来进行不循环比较。
    • 我有一种直觉,用许多小正则表达式循环可能比一个巨大的正则表达式要快。不过需要对其进行基准测试。
    【解决方案2】:

    这不是对您问题的回答,而是对您的问题的回答。

    我了解您的问题与性能有关。

    据我了解,您遇到的主要问题之一是您使用的是 RegEx。让我解释一下我的观点。在 AWK 中,当您使用这样的正则表达式时:/MyRegExp/,您使用的是 RegEx 的编译版本,因此每次您需要检查匹配时,您只需检查它,但是当您像这样使用 RegEx:“MyRegExp”,每次您要检查字符串是否匹配时都会编译它。

    你真的在检查 RegEx 吗?也许你不是,'index'函数对你来说已经足够了。

    您为什么不尝试构建一个脚本并运行它呢?您可以创建如下脚本:

    /pattern1/{
        print
        next
    }
    /pattern2/{
        print
        next
    }
    /pattern3/{
        print
        next
    }
    ...
    ...
    

    然后使用第二个文件运行它。无论如何,我希望它会有所帮助。

    【讨论】:

      【解决方案3】:
      while(getline a < "'$phpath'") PATS["^.*"a".*$"]
      

      RE ^.*"a".*$ 等同于 a。您可以直接使用| 声明 OR 条件,而不是遍历模式。

      如果你的输入文件是

      every
      good
      boy
      does
      fine
      

      你的 RE 变成 every|good|boy|does|fine 并且你的代码被简化为

      $0 ~ pattern { 
          for (i=1; i<=10; i++) {
              if( $i ~ pattern ) { 
                 print "$f" > "$newPath$filename" # what's $f?  
                 break
              }
          }
      }
      

      即先扫描整行。如果它找到了一些东西,就遍历前 10 列。我敢打赌,这比无条件地迭代它们要快。

      【讨论】:

      • 我需要模式,因为列不包含确切的短语。例如:test my phrase test, test, test, test, test, test, test 捕获 my phrase 我需要正则表达式。
      猜你喜欢
      • 2013-10-07
      • 2017-06-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 2020-10-23
      • 1970-01-01
      • 2018-08-08
      • 1970-01-01
      相关资源
      最近更新 更多