【问题标题】:Bash grep -P with a list of regexes from a fileBash grep -P 使用文件中的正则表达式列表
【发布时间】:2020-07-29 17:54:38
【问题描述】:

问题:必须针对多个 PCRE 正则表达式对数百个目录中的数十万个文件进行测试,以对文件进行计数和分类,并确定哪个正则表达式更可行和更具包容性。

我对单个正则表达式测试的方法:

find unsorted_test/. -type f -print0 |
    xargs -0 grep -Pazo '(?P<message>User activity exceeds.*?\:\s+(?P<user>.*?))\s' |
    tr -d '\000' |
    fgrep -a unsorted_test |
    sed 's/^.*unsorted/unsorted/' |
    cut -d: -f1 > matched_files_unsorted_test000.txt ;
wc -l matched_files_unsorted_test000.txt

find | xargs 允许回避 grep 的“参数过多”错误

grep -Pazo 是负责繁重工作的人 -P 用于 PCRE 正则表达式 -a 是为了确保文件被读取为文本,-z -o 仅仅是因为它不适用于我拥有的文件库

tr -d '\000' 是为了确保输出不是二进制的

fgrep -a 是只获取文件名所在的行

sed 是为了抵消 grep 相互附加尾随行的令人敬畏的习惯(基本上删除文件路径之前一行中的所有内容)

cut -d: -f1 只切断文件路径

wc -l 统计匹配文件列表的结果大小

结果是一个包含 10k+ 行的文件,如下所示:unsorted/./2020.03.02/68091ec4-cf04-4843-a4b2-95420756cd53 这是我最终想要的。

显然这不是很好,但是这对于用棍子和泥土制成的东西很有效。我的主要目标是测试概念和正则表达式,而不是计算进一步扩展或任何东西,真的。

所以,由于grep -P 不支持-f 参数,我尝试使用while read 循环:

(while read regexline ;
    do echo "$regexline" ;
    find unsorted_test/. -type f -print0 |
    xargs -0 grep -Pazo "$regexline" |
    tr -d '\000' |
    fgrep -a unsorted_test |
    sed 's/^.*unsorted/unsorted/' |
    cut -d: -f1 > matched_files_unsorted_test000.txt ;
    wc -l matched_files_unsorted_test000.txt |
    sed 's/^ *//' ;
done) < regex_1.txt

正如您所想象的那样 - 它非常失败:所有内容均为零。

我已经尝试过 grep 中的引号、循环类型等。没有。

非常感谢您对当前代码的任何帮助或有关如何执行此操作的建议。 谢谢。

附:是的,我尝试过 pcregrep,但即使在单个模式上它也会返回零匹配项。不知道为什么。

【问题讨论】:

  • tr -d '\000' is to make sure the output is not binary 很奇怪。只是不要使用-z。或使用-a
  • 我知道这很奇怪,但是如果没有-z,我会得到零匹配任何模式,即使我已经使用了-a
  • 我还没有完全理解你的问题,但是如果你想要-P 和多行匹配以及-f 选项以及执行替换,ripgrep 是一个不错的选择......而且,两者GNU grep 和 ripgrep 支持递归搜索,所以你也可以避免find

标签: bash grep pcre


【解决方案1】:

你可以做到这一点,这是不可能的慢:

find unsorted_test/. -type f -print0 |
while IFS= read -d '' -r file; do
     while IFS= read -r regexline; do
        grep -Pazo "$regexline" "$file"
    done < regex_1.txt
done |
tr -d '\000' | fgrep -a unsorted_test... blablabla

或者对于每一行:

find unsorted_test/. -type f -print0 |
while IFS= read -d '' -r file; do
    while IFS= read -r line; do
         while IFS= read -r regexline; do
             if grep -Pazo "$regexline" <<<"$line"; then
                  break
             fi
        done < regex_1.txt
done |
tr -d '\000' | fgrep -a unsorted_test... blablabl

或者也许使用 xargs。

但我相信只需使用| 加入文件中的正则表达式:

find unsorted_test/. -type f -print0 |
{
    regex=$(< regex_1.txt paste -sd '|')
    # or maybe with braces
    # regex=$(< regex_1.txt sed 's/.*/(&)/' | paste -sd '|')
    xargs -0 grep -Pazo "$regex"
} |
....

注意事项:

  • 要从文件中读取行,请使用IFS= read -r lineread-d '' 选项是 bash 语法。
  • 仅在管道之后带有空格、制表符和 cmets 的行将被忽略。您可以将命令放在单独的行中。
  • 使用grep -F 而不是已弃用的fgrep

【讨论】:

  • 感谢您的提示。您建议的方式是相反的:将每个文件与每个正则表达式匹配,这与我想要的完全相反,是的,它会非常慢。然而,IFS= read -r line 成功了——我自己的代码从中受益,现在可以按我的意愿工作!非常感谢!
猜你喜欢
  • 2018-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
相关资源
最近更新 更多