“grep -v -l”相当于“grep -L”吗?

“grep -v -l”相当于“grep -L”吗?

每隔一段时间就会有一个答案(或评论)建议一起使用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}

答案3

让我们使用一个公式来表示每个选项的组合。

  • 有一个包含 n行的文件
  • 行中的文本 i表示为 L(i)
  • match(L(i)) 表示 L(i)与搜索模式匹配

-l: 一个文件,至少有一个匹配行

-l

-L:没有任何匹配的文件

-L

-vl: 一个文件中至少有一个不匹配的行

-vl

-vL:空文件或每行都匹配的文件

-vL

相关内容