首页 > 解决方案 > 打印文本块后退出 AWK 语句

问题描述

我的问题是我有一个非常大的数据库(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以开始循环。例如, patterngroup_1被放入var并开始在数据库中搜索。如果第一列与模式匹配,则将该行保存到文件pattern_matched.file文件中。

目前,它会搜索整个 10GB 的数据并按预期将数据打印到文件中,但这会浪费大量时间。打印与模式匹配的行后,我想停止awk继续搜索数据库并从输入文件转到下一个模式。一个示例行为group_2awk检查数据库的前 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}}'

输出这个: 奇怪的行为

标签: bashmacosawkscriptingtext-manipulation

解决方案


你的外壳代码:

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

或类似的。


推荐阅读