我有一组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" -- "{}"'