改善Shell脚本性能

时间:2009-07-08 12:31:11

标签: regex performance unix shell grep

如果$2包含模式$line,则此shell脚本用于从$line中提取一行数据。

[A-Z0-9.-]+@[A-Z0-9.-]+使用正则表达式$1(简单的电子邮件匹配)构建,形成文件#! /bin/sh clear for line in `cat "$1" | grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+"` do echo `cat "$2" | grep -m 1 "\b$line\b"` done 中的行。

$1

文件$2 包含短行数据(< 100 chars)并包含约。 50k行(约1-1.5 MB)。

文件{{1}} 的文字行数稍长(> 80到<200),并且有2M +行(约200MB)。

运行的台式机有足够的RAM(6 Gig)和1-2核的Xenon处理​​器。

是否有任何快速修复可以提高性能,因为目前完全运行需要1-2个小时(并输出到另一个文件)。

注意:我对所有建议持开放态度,但我们不能复杂地重写整个系统等。此外,数据来自第三方,并且易于随机格式化。< / em>的

5 个答案:

答案 0 :(得分:7)

快速建议:

  1. 避免使用useless use of cat并将cat X | grep Y更改为grep Y X

  2. 您可以处理grep输出,因为它是通过管道而不是使用反引号生成的。使用反引号需要先完成grep才能开始第二个grep

  3. 因此:

    grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+" "$1" | while read line; do
        grep -m 1 "\b$line\b" "$2"
    done
    

    下一步:

    1. 不要反复处理$2。很大。您可以保存所有模式,然后对文件执行单个grep。
    2. 将循环替换为sed
    3. 不再重复grep

      grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+" "$1" | sed -E 's/^|$/\\1/g' > patterns
      grep -f patterns "$2"
      

      最后,使用一些bash幻想(参见man bash→流程替换),我们可以抛弃临时文件,并在一个长行中执行此操作:

      grep -f <(grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+" "$1" | sed -E 's/^|$/\\b/g') "$2"
      

      除非你有很多模式grep -f内存和barf耗尽,否则这很好。如果发生这种情况,您需要批量运行它。讨厌,但可行:

      grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+" "$1" | sed -E 's/^|$/\\1/g' > patterns
      
      while [ -s patterns ]; do
          grep -f <(head -n 100 patterns) "$2"
          sed -e '1,100d' -i patterns
      done
      

      这将一次处理100个模式。它可以立即做的越少,你的第二个文件就越少。

答案 1 :(得分:3)

问题是你管道太多shell命令,以及不必要地使用cat。

使用awk

的一种可能的解决方案
awk 'FNR==NR{
    # get all email address from file1
    for(i=1;i<=NF;i++){
        if ( $i ~ /[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+/){
            email[$i]
        }
    }
    next
}
{
 for(i in email) {
    if ($0 ~ i) {
        print 
    }
 }
}' file1 file2

答案 2 :(得分:2)

如果$ 1是文件,请不要使用“cat | grep”。相反,将文件直接传递给grep。应该看起来像

grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+" $1

此外,您可能需要调整正则表达式。您至少应该期望电子邮件地址中的下划线(“_”),所以

grep -i -o -E "[A-Z0-9._-]+@[A-Z0-9.-]+" $1

答案 3 :(得分:2)

我会把循环取出来,因为花费200万行文件50k次可能相当昂贵;)

允许你取消循环 首先使用外部grep命令创建所有电子邮件地址的文件。 然后使用它作为模式文件,使用grep -f

来执行辅助grep

答案 4 :(得分:1)

正如John Kugelman已经回答的那样,通过管道而不是使用反引号来处理grep输出。如果你使用反引号,则首先运行反引号中的整个表达式,然后使用反引号的输出作为参数运行外部表达式。

首先,这将比必要的慢得多,因为管道将允许两个程序同时运行(如果它们都是CPU密集型且你有多个CPU,这真的很好)。然而,还有另一个非常重要的方面,即行

for line in `cat "$1" | grep -i -o -E "[A-Z0-9.-]+@[A-Z0-9.-]+"`

可能会变得很长时间来处理shell。大多数shell(据我所知)限制命令行的长度,或者至少是命令的参数,我认为这也可能成为for循环的问题。