逻辑运算符可以与 find 和 xargs 一起使用吗?

Can logical operators be used with find and xargs?

我有一个包含大约 5000 个文件的目录,其中一些文件是因语法错误而被错误写入的。我正在使用以下代码来识别哪些文件有错误:

ls -1 | while read a; do grep -q '^- ' $a || echo $a; done

我最初尝试使用 findxargs 的组合,但我不知道如何添加我需要的布尔逻辑。

我的用例未 I/O 绑定并且完成速度足够快。但我很好奇是否可以在不依赖 bash 循环的情况下完成同样的操作。尽管对 Bash 很满意,但我倾向于严重依赖管道进入循环,这通常会导致 .

您可以在 find:

中使用布尔逻辑
find -maxdepth 1 -type f \( -exec grep -q '^- ' {} \; -o -print \)

-o 选项是逻辑或。如果 -exec 执行的命令将 return 一个 non-zero return 值 -print 将打印文件名。

这是另一种方法,使用 grep -L:

find -maxdepth 1 -type f -exec grep -L '^- ' {} \;

上面的代码将列出目录中的所有文件,这些文件的内容中不包含以破折号 + space - 开头的行。

要使上面的代码递归(即,将搜索扩展到所有子目录),只需删除 -maxdepth 1 部分。

来自 man grep 关于选项 -L:

-L, --files-without-match Suppress normal output; instead print the name of each input file from which no output would normally have been printed. The scanning will stop on the first match.

单独使用 grep 就足够了:

grep -d skip -L '^- ' *

注意:与 find 不同,这不会自动包含 隐藏 文件。
要递归搜索 ,请改用 grep -L '^- ' -R .(尽管 -R 不是 POSIX-compliant,它适用于 GNU 和 BSD/macOS grep).

-L,如 中所述,打印每个输入文件的 路径 (按指定)not 包含搜索词。

-d skip 跳过目录(虽然选项 -d 不是 POSIX-compliant,GNU 和 BSD/macOS grep 都支持它)。


警告: 正如 hek2mgl 在评论中指出的那样, * [=] 的文件名扩展后产生的命令行61=]可能太长,导致出现/usr/bin/grep: Argument list too long.
等错误 (相比之下,如果使用 -R . 进行 grep 递归搜索,则不会遇到此问题。)

最大值。长度是platform-specific,可以用getconf ARG_MAX查询,不过注意实际限制是低于,具体取决于您环境的大小 - 请参见this article

实际上,5000 个文件可能不会有问题,即使在最大数量相对较低的平台上也是如此。长度,例如 macOS - 除非你有特别长的文件名 and/or 你的 globbing 模式有一个很长的路径组件 [1] .
最近的 Linux 版本有更高的限制。

如果您确实达到了限制并且必须解决它,使用xargs如下:

printf '%s[=11=]' * | xargs -0 grep -d skip -L '^- '

请注意,虽然 -0 读取 NUL-terminated 输入不是 POSIX-compliant,但 GNU 和 BSD/macOS xargs.[=39 都支持它=]

如果输入文件名确实不适合 单个 命令行,xargs 将以最少 [=12] 的方式对输入进行分区=] 处理所有这些调用所必需的调用。


[1] macOS 10.12 有 262,144 字节 (256 KB) 的限制;如果我们保守地假设,在扣除环境的大小和命令行的固定部分后,我们的文件名列表得到 250,000 字节,这给我们每个文件名 250000 / 5000 == 50 字节 + space(列表分隔符),这样每个文件名最多可以有 49 个字节。
相比之下,Ubuntu 16.04 的限制高出 8 倍:2,097,152 字节 (2 MB)。