每隔一段时间就会有一个答案(或评论)建议一起使用grep
's-v
和-l
开关而不是 the -L
(特别是当后者不可用时)。作者似乎相信它们是等价的。
那么,它们可以互换吗? IOW,会grep -v -l
产生与 相同的结果吗grep -L
?
答案1
不,它们不相等,因此结果将几乎总是1有所不同。
让我们看看每个开关的作用:
‘-L’
‘--files-without-match’
Suppress normal output; instead print the name of each input file
from which no output would normally have been printed.
这应该很简单:
grep -L 'pattern' ./*
打印不包含任何匹配行的每个文件的名称'pattern'
。
‘-v’
‘--invert-match’
Invert the sense of matching, to select non-matching lines.
这也应该非常简单:打印每个文件中
grep -v 'pattern' ./*
不匹配的所有行。'pattern'
‘-l’
‘--files-with-matches’
Suppress normal output; instead print the name of each input file
from which output would normally have been printed.
同样,很简单:
grep -l 'pattern' ./*
打印每个包含至少一行匹配的文件的名称'pattern'
。
现在,结合-v
和会发生什么-l
?它与 没有太大区别grep -l
,只是它选择不匹配的行,因此
grep -vl 'pattern' ./*
打印包含至少一行不匹配的每个文件的名称'pattern'
。
所以,请注意差异:
grep -L
打印文件名仅当没有行匹配时 'pattern'
grep -vl
打印文件名仅当至少有一行不匹配时 'pattern'
1:
根据上面的内容,很容易看出这两个命令何时可以产生相同的输出:
-当文件不为空并且全部行与模式匹配 - 在这种情况下将不会有输出
- 或者当文件不为空并且不行与模式匹配 - 在这种情况下它将被列出
这个故事的寓意是:永远不要用来grep -vl
模仿grep -L
。
如果您grep
不支持-L
,这是模拟它的正确方法。
答案2
作为额外说明,在grep
不支持该-L
选项(GNU 扩展)的系统上,使用-vl
@don_crissti 所示的方法不会有帮助,您需要求助于 shell 循环。相当于
grep -L pattern ./*
将会:
for file in ./*; do
{
grep -q pattern || printf '%s\n' "$file"
} < "$file"
done
您会注意到重定向是整体完成的{...}
,而不是像grep
我们不希望在无法打开文件时打印文件名那样。
对于等效项grep -ZL pattern ./*
(如果您希望能够可靠地后处理该输出,则需要它),请替换%s\n
为%s\0
上面的内容。
对于 GNU 的等效项grep -rL pattern .
,您可以执行以下操作:
find . -type f ! -exec grep -q pattern {} \; -print
虽然它也会打印不可读的文件的名称(-readable
find
可以使用谓词,但它也是一个 GNU 扩展,所以很可能如果你grep
不支持-L
,你find
也不会支持-readable
)。
和zsh
:
for f (./**/*(DN.)) {{grep -q pattern || print -r $f}<$f}