【问题标题】:Exiting an AWK statement after printing a block of text打印文本块后退出 AWK 语句
【发布时间】:2023-04-09 12:58:02
【问题描述】:

我的问题是我有一个非常大的数据库 (10GB),我想节省尽可能多的时间来搜索它。我有一个 awk 语句正在搜索数据库并根据模式将数据写入另一个文件。
我有一个输入文件,它将作为终端参数变量输入到我的脚本中。其中有几行数据将用作awk 语句的模式。
在数据库中,与模式匹配的所有行都排在彼此旁边,因此基本上,打印后,无需进一步搜索数据库,因为所有内容都已找到。一旦awk 找到第一个模式匹配行,所有其他模式匹配行都在它之后。

这个问题很难用语言来解释,所以我创建了一些示例来说明我的文件、代码和数据库的外观和操作。

通过终端输入的文件如下所示:

group_1
group_2
group_3
...

10GB 的数据库如下所示:

group_1 DATA ...
group_1 DATA ...
group_1 DATA ...
group_2 DATA ...
group_2 DATA ...
group_2 DATA ...
group_2 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
...

带有awk 语句的脚本代码如下所示:

IFS=$'\n'
set -f
for var in $(cat < "$1")
do  
    awk -v seq="$var" '{if (match($1, seq)) {print $0}}' filepath/database  > pattern_matched.file
done

对这段代码所做的简单解释是,它接受终端参数变量,在这种情况下是一个文件名,并打开它以供for loop 开始循环。例如,模式group_1 被放置在var 中,然后开始搜索数据库。如果第一列与模式匹配,则将该行保存到文件pattern_matched.file 文件中。

目前,它会搜索整个 10GB 的数据并按预期将数据打印到文件中,但这会浪费很多时间。打印与模式匹配的行后,我想停止 awk 继续搜索数据库并从输入文件转到下一个模式。 group_2 的一个示例行为是 awk 检查数据库的前 3 行并发现没有任何行具有匹配的模式。但是,第 4 行包含该模式,因此它会打印该行和其后的后续模式匹配行。当awk 到达第8 行时,它退出awk 语句,然后for loop 可以迭代到要搜索的下一个模式group_3

awk '{print $0; exit}' filename

这样的东西不起作用,因为它只打印第一个实例并爆发,我想要一些可以打印所有匹配项的东西,一旦找到下一个非模式匹配,它就会爆发。

提前致谢。

更新: 现在的问题是,下面给出的解决方案是合乎逻辑的。如果它进入 if 语句,它将将该行打印到文件中并迭代到下一行。如果该行不匹配,它将进入 else-if 语句并退出 awk。这对我来说很有意义,但由于某种原因,一旦 flag 变量被第一个匹配行的 if 语句设置为 1,它就会进入 else-if 语句。由于 else-if 条件的计算结果为真,它甚至在扫描下一行之前就退出了。我在awk 语句中的任何地方都使用打印语句确认了这种行为。 这是我的带有打印语句的代码:

awk -v seq="$seqid" '{if(match($1, seq)) {print "matched" ; print $1 ; flag=1} else if (flag) {print "not matched" ; exit}}'

输出这个: weird behavior

【问题讨论】:

  • 为什么不添加-v haveread=0 那么你的语句可以是:'{if (match($1, seq)) {print $0; haveread=1} else {haveread == 1 &amp;&amp; exit}}'(或类似的使用标志 valraible)这将导致awkexit 在序列之后的第一行它读取的行。在此之前,haveread=0 并且在 else 子句中没有采取任何行动。
  • haveread == 1 &amp;&amp; exit 是指haveread == 1 ; exit 吗?存在一些语法问题。
  • 喜欢awk -v rd=0 '{ if ($1 == seq) {print $1; rd = 1} else {if (rd == 1) exit }}'(使用rd作为标志)
  • 这几乎可以工作。它打印第一行并自动点击 else-if 并退出。为什么?我不知道,else-if 的定义应该已经停止了它,但我只是在我的代码中确认了它:awk -v seq="$seqid" -v flag=0 '{if(match($1, seq)) {print $1 ; flag=1 } else {if(flag == 1) exit}}'
  • @Lefty 请参阅 awk.freeshell.org/AllAboutGetline 了解为什么不这样做 that

标签: bash macos awk scripting text-manipulation


【解决方案1】:

你不能把输入文件 (input_file) 读入 awk:

$ cat input_file
group_1
group_3

Awk 脚本:

$ awk 'NR==FNR{a[$0];next} $1 in a' input_file database
group_1 DATA ...
group_1 DATA ...
group_1 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...

【讨论】:

    【解决方案2】:

    你的外壳代码:

    for var in $(cat < "$1")
    do  
        awk 'script' filepath/database  > pattern_matched.file
    done
    

    正在使用反模式读取存储在$1 中的输入文件,请参阅http://mywiki.wooledge.org/BashFAQ/001,并将在循环的每次迭代中覆盖pattern_matched.file。我怀疑你应该把它写成:

    while IFS= read -r var
    do  
        awk 'script' filepath/database  
    done < "$1" > pattern_matched.file
    

    您的 awk 代码:

    awk -v seq="$var" '{if (match($1, seq)) {print $0}}'
    

    不必要地使用match(),因为您只想进行正则表达式比较并且没有使用 match() 填充的变量来帮助您隔离匹配的字符串 (RSTART/RLENGTH),并且它使用了默认的空条件和然后将真实条件放入动作空间,然后硬编码打印当前记录的默认动作。相当于:

    awk -v seq="$var" '$1 ~ seq'
    

    但我不相信您确实需要正则表达式比较 - 鉴于您的示例,您应该改为进行字符串比较:

    awk -v seq="$var" '$1 == seq'
    

    鉴于您发布的示例可能会产生误导,您只需根据您是否需要正则表达式或字符串以及 $1 上的部分匹配或完全匹配来选择其中哪一个是合适的:

    awk -v seq="$var" '$1 == seq'              # full string
    awk -v seq="$var" 'index($1,seq)'          # partial string
    awk -v seq="$var" '$1 ~ ("^"seq"$")'       # full regexp
    awk -v seq="$var" '$1 ~ seq'               # partial regexp
    

    假设我们使用第一个完整的字符串匹配匹配,然后在处理完匹配的 $1 后退出:

    awk -v seq="$var" '$1 == seq{print; f=1; next} f{exit}'
    

    这将使您的完整代码:

    while IFS= read -r var
    do  
        awk -v seq="$var" '$1 == seq{print; f=1; next} f{exit}'  filepath/database  
    done < "$1" > pattern_matched.file
    

    但我怀疑你是否需要一个 shell 循环,你可以这样做:

    awk 'NR==FNR{seqs[$1]; next} $1 in seqs' "$1" filepath/database > pattern_matched.file
    

    或其他一些只有 awk(或者可能只是 join)的变体读取输入文件一次。你可以在所有seqs[]都被处理后退出:

    awk '
        NR==FNR { seqs[$1]; numSeqs++; next }
        $1 in seqs { print; if ($1 !== prev) numSeqs--; prev = $1; next }
        numSeqs == -1 { exit }
    ' "$1" filepath/database > pattern_matched.file
    

    或类似的。

    【讨论】:

      【解决方案3】:

      我认为这应该可以解决问题:

      awk -v seq="$var" '{if (match($1, seq)) {print $0; found=1} else if (found) { exit }}'
      

      类似于 David C. Rankin 的答案,但无需将 rd=0 参数传递给 awk,因为在 awk 中,任何未初始化的变量在首次使用时都会被初始化为零。

      【讨论】:

      • hmm,情况和上面一样,在打印第一条匹配行并将found设置为1后,它直接进入else-if语句并退出。也许我的其余代码出了点问题,导致它跳到 else-if 语句中????老实说,我很难理解为什么 awk 决定进入 else-if 语句,如果它已经在 if 之前......
      • 很奇怪。你想检查这个例子,看看你的情况有什么不同吗? pastebin.com/HjkyU3XJ
      • 我看了一下粘贴,我们基本上有相同的代码,我把它放到我的脚本中,输出是:&gt;1_AE00496_00001 exit in 2 row --- &gt;2135_AE01682_00001 exit in 13094 row ---ummmm
      • 我在tutorialspoint.com/execute_bash_online.php这里执行了脚本,结果是预期的。
      【解决方案4】:

      由于我们并不真正知道您打算对您的程序做什么,所以我只会给您一个 awk 解决方案:

      awk -v seq="$var" '($1!=seq) { if(p) exit; next }($1==seq){p=1}p'
      

      这使用标志p 来检查它是否已经满足序列seq。一个简单的 if 条件确定它是否应该退出 awk 或移动到下一条记录。找到seq后退出,之前完成移动到下一条记录。

      但是,由于您将它放在一个循环中,这将一遍又一遍地读取文件。如果要进行子选择,可以使用James Brown的解决方案

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-16
        • 1970-01-01
        • 2021-03-23
        • 1970-01-01
        • 2014-08-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多