我的问题是我有一个非常大的数据库(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
对此代码的作用的简要解释是,它接受了Terminal参数变量(在这种情况下为文件名),并将其打开以供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条件的值为true,因此它甚至在扫描下一行之前就退出了。我在awk
语句中到处都用print语句确认了此行为。
这是我的带有打印语句的代码:
awk -v seq="$seqid" '{if(match($1, seq)) {print "matched" ; print $1 ; flag=1} else if (flag) {print "not matched" ; exit}}'
输出以下内容: weird behavior
答案 0 :(得分: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 ...
答案 1 :(得分:1)
您的外壳代码:
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),而是使用了无效的null条件,然后将实际条件放在操作空间中,然后硬编码打印当前记录的默认操作。等同于:
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
或类似的
答案 2 :(得分:0)
我认为这应该可以解决问题:
awk -v seq="$var" '{if (match($1, seq)) {print $0; found=1} else if (found) { exit }}'
类似于David C. Rankin的答案,但是不需要将rd=0
参数传递给awk,因为在awk中,任何未初始化的变量在首次使用时都会初始化为零。
答案 3 :(得分:0)
由于我们并不十分了解您打算如何使用程序,因此我将为您提供awk解决方案:
awk -v seq="$var" '($1!=seq) { if(p) exit; next }($1==seq){p=1}p'
这使用标志p
来检查它是否已经符合序列seq
。一个简单的if条件确定是退出awk还是移至下一条记录。找到seq后完成退出,之前完成移至下一条记录。
但是,由于将其置于循环中,因此将一遍又一遍地读取文件。如果要进行子选择,则可以使用James Brown
的解决方案