让 grep 处理特殊文件名

让 grep 处理特殊文件名

我有一组txt文件,其名称可能包含空格或特殊字符,例如#.

我有一个grep解决方案grep -L "cannot have" $(grep -l "must have" *.txt)来列出所有具有must have但不具有cannot have.

例如,有一个文件abc defg.txt仅包含 1 行:must have.

所以通常 grep 解决方案应该找出abc defg.txt,但它返回:

grep: abc: No such file or directory
grep: defg.txt: No such file or directory

我认为对于包含 的文件名#,grep 解决方案也是无效的。

谁能帮我修改 grep 解决方案吗?

答案1

如果如果你愿意走得更远,awk 可以一次性完成:

awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0} 
  /must have/{a=1} /cannot have/{b=1} END{s()}' filepattern

对于最近的 gawk,您可以使用 BEGINFILE 和 ENDFILE 进行简化。 (像所有 awk 答案一样,您可以使用 -f 将 awk 命令放入文件中,并且像大多数一样,如果您愿意,您可以轻松转换为 perl。)

答案2

由于您已经在使用 GNU 特定选项 ( -L),您可以这样做:

grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"

这个想法是用于-Z打印 NUL 分隔的文件名列表,并用于xargs -r0将该列表作为参数传递给第二个grep.

默认情况下,命令替换按空格、制表符和换行符(以及 中的 NUL zsh)进行分割。类似于 Bourne 的 shellzsh也对分裂产生的每个单词执行通配符操作。

你可以这样做:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )

但这仍然会破坏包含换行符的文件名。

zsh(且zsh仅),您可以执行以下操作:

IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

或者:

grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}

答案3

考虑使用shell 命令来find代替:grep

find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'

相关内容