【问题标题】:Can logical operators be used with find and xargs?逻辑运算符可以与 find 和 xargs 一起使用吗?
【发布时间】:2017-05-31 16:37:42
【问题描述】:

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

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

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

我的用例不受 I/O 限制并且完成速度足够快。但是我很好奇是否可以在不依赖 bash 循环的情况下完成相同的操作。虽然对 Bash 很满意,但我倾向于严重依赖管道进入循环,这通常会导致 mind numbingly slow performance

【问题讨论】:

  • 你到底想达到什么目的?显示包含字符串的文件名?
  • 显示不匹配这个正则表达式的文件。关键点是布尔逻辑 OR。

标签: bash loops find xargs


【解决方案1】:

您可以在find 中使用布尔逻辑:

find -maxdepth 1 -type f \( -exec grep -q '^- ' {} \; -o -print \)

-o 选项是逻辑或。如果-exec 执行的命令将返回一个非零返回值-print 将打印文件名。

【讨论】:

  • 谢谢;这完美地工作。我稍微摆弄了一下,发现如果我也添加-mindepth 1,那么它将避免将当前文件夹(.)与其余结果一起输出。
  • 是的,但是-type f 无论如何都应该排除当前文件夹(.)
  • 您能解释一下为什么需要括号来避免 (.) 吗?
  • 因为. 是一个文件夹,意思是-type f 失败。这导致-print 被执行。您可以将这一行(不带括号)读作-maxdepth 1 && -type f && -exec ... || -print
  • 这很有意义。谢谢你的澄清。
【解决方案2】:

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

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

上面的代码将列出目录中不包含以破折号 + 空格 - 开头的行的所有文件。

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

来自man grep 关于选项-L

-L, --files-without-match 抑制正常输出;而是打印通常没有输出的每个输入文件的名称 打印。扫描将在第一次匹配时停止。

【讨论】:

    【解决方案3】:

    单独使用grep 就足够了:

    grep -d skip -L '^- ' *
    

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

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

    -d skip 跳过目录(虽然选项 -d 不符合 POSIX 标准,但 GNU 和 BSD/macOS grep 都支持它)。


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

    最大值。长度是特定于平台的,可以通过getconf ARG_MAX查询,但请注意实际限制低于,具体取决于您的环境大小 - 请参阅this article

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

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

    printf '%s\0' * | xargs -0 grep -d skip -L '^- '
    

    请注意,虽然-0 读取以 NUL 结尾的输入不符合 POSIX 标准,但 GNU 和 BSD/macOS xargs 都支持。

    如果输入文件名确实不适合单个 命令行,xargs 将以导致处理所有文件所需的最少grep 调用的方式对输入进行分区.


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

    【讨论】:

    • 问题在于全局。正如 OP 所说,该文件夹中有 5000 个文件。这会给你一个argument list too long 错误。您需要使用-r(或-R
    • @mklement0 很好的答案。 +2(不幸的是,这是不允许的)我很惊讶 5000 不是问题。我知道限制是可配置的,但我低估了它的(默认)值。
    • @hek2mgl:谢谢。我添加了一个计算作为脚注。根据in-ulm.de/~mascheck/various/argmax 的说法,在 Linux 和 macOS 等现代平台上,您必须重新编译内核以增加 ARG_MAX 限制
    猜你喜欢
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 2016-05-11
    • 2016-10-02
    • 1970-01-01
    相关资源
    最近更新 更多