grep提前停止,每个模式一个匹配

时间:2016-01-11 18:23:31

标签: awk grep

假设我有一个模式所在的文件,例如的 patterns.txt 即可。而且我知道所有模式只会在另一个文件 patterns_copy.txt 中匹配一次,在这种情况下,简单起见只是 patterns.txt 的副本。

如果我跑

grep -m 1 --file=patterns.txt patterns_copy.txt > output.txt

我只得到一行。我想是的,因为一旦两个文件的第一行匹配, m 标志就会停止整个匹配过程。

我想要实现的是让 patterns.txt 中的每个模式只匹配一次,然后让grep移动到下一个模式。

我如何实现这一目标?

感谢。

2 个答案:

答案 0 :(得分:2)

更新了答案

我现在有机会将我在考虑awk的内容整合到GNU Parallel概念中。

我使用/usr/share/dict/words作为我的patterns文件,其中有235,000行。在另一个答案中使用BenjaminW的代码,花了141分钟,而这段代码却缩短到11分钟。

这里的区别在于没有临时文件,awk一旦找到它正在寻找的所有8个东西就可以停止...

#!/bin/bash

# Create a bash function that GNU Parallel can call to search for 8 things at once
doit() {
   # echo Job: $9
   # In following awk script, read "p1s" as a flag meaning "p1 has been seen"
   awk -v p1="$1" -v p2="$2" -v p3="$3" -v p4="$4" -v p5="$5" -v p6="$6" -v p7="$7" -v p8="$8" '
      $0 ~ p1 && !p1s {print; p1s++;}
      $0 ~ p2 && !p2s {print; p2s++;}
      $0 ~ p3 && !p3s {print; p3s++;}
      $0 ~ p4 && !p4s {print; p4s++;}
      $0 ~ p5 && !p5s {print; p5s++;}
      $0 ~ p6 && !p6s {print; p6s++;}
      $0 ~ p7 && !p7s {print; p7s++;}
      $0 ~ p8 && !p8s {print; p8s++;}
      {if(p1s+p2s+p3s+p4s+p5s+p6s+p7s+p8s==8)exit}
   ' patterns.txt

}
export -f doit

# Next line effectively uses 8 cores at a time to each search for 8 items
parallel -N8 doit {1} {2} {3} {4} {5} {6} {7} {8} {#} < patterns.txt

只是为了好玩,这就是它对我的CPU的作用 - 蓝色意味着最大化,看看你是否可以在绿色CPU历史中看到作业的开始位置!

enter image description here

其他想法

以上好处是输入文件排序相对较好,因此值得一次查找8件事,因为它们很可能在输入文件中彼此接近,因此我可以避免相关的开销为每个寻求的术语创建一个过程。但是,如果您的数据排序不好,这可能意味着您需要花费大量时间查看文件,而不是查找下一个或其他6个项目所需的时间。在这种情况下,你可能会更好:

parallel grep -m1 "{}" patterns.txt < patterns.txt

原始答案

看了一下文件的大小,我现在认为awk可能不是可行的方法,但GNU Parallel可能就是这样。我尝试了两种方法并行化问题。

首先,我通过输入文件一次搜索8个项目,这样我就可以使用grep参数搜索第二组-m 1

其次,由于我有CPU核心,我并行执行这些“8-at-a-time” grep的多个。

我使用GNU并行作业号{#}作为唯一的临时文件名,并且一次只创建16个(或者多个CPU核心)临时文件。临时文件的前缀为ss(用于子搜索),因此在测试时可以轻松地调用它们。

加速似乎是我机器上的4倍。我使用/usr/share/dict/words作为我的测试文件。

#!/bin/bash

# Create a bash function that GNU Parallel can call to search for 8 things at once
doit() {
   # echo Job: $9
   # Make a temp filename using GNU Parallel's job number which is $9 here
   TEMP=ss-${9}.txt
   grep -E "$1|$2|$3|$4|$5|$6|$7|$8" patterns.txt > $TEMP
   for i in $1 $2 $3 $4 $5 $6 $7 $8; do
      grep -m1 "$i" $TEMP
   done
   rm $TEMP

}
export -f doit

# Next line effectively uses 8 cores at a time to each search for 8 items
parallel -N8 doit {1} {2} {3} {4} {5} {6} {7} {8} {#} < patterns.txt

答案 1 :(得分:1)

你可以像这样循环你的模式(假设你正在使用Bash):

while read -r line; do
    grep -m 1 "$line" patterns_copy.txt
done < patterns.txt > output.txt

或者,在一行中:

while read -r line; do grep -m 1 "$line" patterns_copy.txt; done < patterns.txt > output.txt

对于并行处理,您可以将进程作为后台作业启动:

while read -r line; do
    grep -m 1 "$line" patterns_copy.txt &
    read -r line && grep -m 1 "$line" patterns_copy.txt &
    # Repeat the previous line as desired
    wait # Wait for greps of this loop to finish
done < patterns.txt > output.txt

这并不是很优雅,因为每个循环都会等待最慢的grep完成,但仍然应该比每个循环只有一个grep更快。