在文件中查找多个单词模式

在文件中查找多个单词模式

我在 filesdir 文件夹中有大约 50000 个文件 (.txt) 和更多项目。这些文件中的值:“fax”、“phone”、“address”以不同的配置显示。我需要查找所有包含“fax”和“phone”但不包含“address”的文件。我尝试使用几个 grep 命令进行 for 循环。ls 给出“参数太多”。所以我尝试了:

find /filesdir/ -maxdepth 1 -name '*.txt' -exec grep -l 'fax' \; grep -l 'phone' \; grep -l -v 'address'

为什么它不起作用?

答案1

git grep

您可以使用git grep布尔表达式组合多个模式,例如:

git grep --all-match --no-index -e "fax" --and -e "phone" --and --not -e "address"

您可以将不同的图案与布尔值诸如--and--or和 之类的表达式--not

--all-match当给出多个模式表达式时,指定此标志以将匹配限制为包含所有行的文件

--no-index 搜索当前目录中不受 Git 管理的文件。

-l//--files-with-matches--name-only显示文件的名称。

-e下一个参数是模式。默认使用基本正则表达式。

其他需要考虑的参数:

--threads要使用的 grep 工作线程的数量。

-q//--quiet--silent输出匹配的行;匹配时以状态 0 退出。

要更改图案类型,您还可以使用-G/ --basic-regexp(默认)、-F/ --fixed-strings-E/ --extended-regexp-P/ --perl-regexp-f file和其他。

查找man git-grep更多帮助。

grep

以下是grep使用链式语法命令替换

grep -L "address" $(grep -l "phone" $(grep -rl "fax" .))

解释:

  1. 查找具有“传真”模式的文件名 ( grep -rl "fax" .)。
  2. 过滤器找到具有“电话”模式的文件名(grep -l "phone" $(cmd))。
  3. 进一步过滤以排除不包含addressgrep -L "address" $(cmd))的文件。

如果你正在处理大数据,请考虑使用ripgrep反而。

find

上面的例子可能不适用于带有空格的文件,所以这里是带有的版本find

find . -type f -name '*.txt' \
  -execdir bash -c 'grep -L "address" "$(grep -l "phone" "$(grep -l "fax" "{}")")"' ';' \
2>/dev/null

也可以看看:检查文件中是否存在多个字符串或正则表达式

答案2

有几个原因会导致它不起作用:

  • 您省略{}-exec
  • 您正在尝试通过一次调用执行-exec多个命令grep
  • 我怀疑你的逻辑有缺陷,因为 find 的默认操作是合乎逻辑的,AND而你可能想要faxOR phoneANDnot address

我还没有完全测试过,但我想你想要更多类似的东西

find /filesdir/ -maxdepth 1 -name '*.txt' -exec grep -q 'fax\|phone' {} \; -exec grep -lv 'address' {} \;

答案3

在每个文件的一行上打印文件名及其内容

我认为这个命令行可以做到这一点:

find -maxdepth 1 -name "*.txt" -exec echo "{} :" \; -exec cat {} \; -exec echo EOF \;| tr '\n' ' '|sed 's/EOF /\n/g'|grep -iv 'address'|grep -i 'fax'|grep -i 'phone'

解释:

  • 对于每个文件(由 找到find

    • 回显文件名
    • 打印内容
    • 打印文件结束标志(应该与文件内部的内容不同。请谨慎选择此标志!我使用 EOF,您可能需要其他东西。
  • 对于整个输出

    • 将换行符转换为空格,使所有内容都显示在一行上
    • 将文件结束标志转换为换行符

    现在每个文件的内容都在单独的一行中,适合grep

  • 最后

    • 跳过包含“地址”的行
    • 从剩余的输出中选择包含“fax”的行
    • 从剩余输出中选择包含“phone”的行

仅打印文件名

上面的命令行打印文件名和文件内容(合并为一行),这有利于测试,但不适合处理数千个文件。

以下命令行仅打印文件名。它使用“:::”将每个文件名与文件内容分隔开。

find -maxdepth 1 -name "*.txt" -exec echo "{} :::" \; -exec cat {} \; -exec echo EOF \;| tr '\n' ' '|sed 's/EOF /\n/g'|grep -iv 'address'|grep -i 'fax'|grep -i 'phone' | sed 's/ :::.*//'

答案4

查找不包含该模式的文件(兼容包含空格/或换行符的文件)address

find -type f ! -exec grep -q 'address' {} \; -print 

并仅打印那些包含模式的fax phone在整个文件中以任何顺序排列:

find -type f ! -exec grep -q 'address' {} \; \
               -exec grep -qP '(?s)(?=.*?fax)(?=.*?phone)' {} \; -print

或者 POSIXly:

find -type f ! -exec grep -q 'address' {} \; \
               -exec grep -q 'fax' {} \; \
               -exec grep -q 'phone' {} \; -print

\n或者假设文件名中没有ewline,那么:

grep -lP '(?s)(?=.*?fax)(?=.*?phone)' * |xargs -d'\n' grep -L address
  • (?=pattern):正向前瞻:正向前瞻结构是一对括号,左括号后跟问号和等号。

  • (?s)已知的“dot-all”告诉grep让点也.能够匹配ewline 字符。\n

  • 表示.*?匹配.出现零次或多次的任意字符*,且这些字符是可选的,后跟一个模式(faxphone)。表示匹配?其前的所有内容都是可选的(表示匹配的所有内容出现零次或一次.*

未来阅读:

正则表达式前瞻、后瞻和原子组

相关内容