find -print0 和 xargs -0 开关是不是有 grep 等效项?

Posted

技术标签:

【中文标题】find -print0 和 xargs -0 开关是不是有 grep 等效项?【英文标题】:Is there a grep equivalent for find's -print0 and xargs's -0 switches?find -print0 和 xargs -0 开关是否有 grep 等效项? 【发布时间】:2013-04-05 06:55:15 【问题描述】:

我经常想写这样的命令(在zsh,如果相关的话):

find <somebasedirectory> | \
    grep stringinfilenamesIwant | \
    grep -v stringinfilesnamesIdont | \
    xargs dosomecommand

(或更复杂的 grep 组合)

近年来find 增加了-print0 开关,xargs 增加了-0,它允许通过空终止文件名以优雅的方式处理名称中包含空格的文件,允许这样做:

find <somebasedirectory> -print0 | xargs -0 dosomecommand

但是,grep(至少我拥有的版本,Ubuntu 上的 GNU grep 2.10)似乎没有等效于使用 生成以空字符结尾的行;它有 --null,但这似乎只与使用 grep 直接在文件中搜索时使用 -l 输出名称有关。

是否有可以与 grep 一起使用的等效选项或选项组合?或者,是否有一种简单而优雅的方式来简单地使用 find 的-regex 或 Perl 来表达我的命令管道?

【问题讨论】:

-print0 选项通常只需要处理包含换行符而不是其他空格的文件名,因为传统的换行符分隔符(与-print 一起使用)对它们很好。 【参考方案1】:

使用 GNU Grep 的 --null 标志

根据GNU Grep documentation,您可以使用Output Line Prefix Control 来处理ASCII NUL 字符,方法与findxargs 相同。

-Z --null 输出一个零字节(ASCII NUL 字符)而不是通常跟在文件名后面的字符。例如,‘grep -lZ’在每个文件名之后输出一个零字节,而不是通常的换行符。此选项使输出明确,即使存在包含不寻常字符(如换行符)的文件名。此选项可与“find -print0”、“perl -0”、“sort -z”和“xargs -0”等命令一起使用,以处理任意文件名,甚至包含换行符的文件名。

使用来自 GNU Coreutils 的 tr

正如 OP 正确指出的那样,此标志在处理输入或输出的文件名时最有用。为了将 grep 输出实际转换为使用 NUL 字符作为行尾,您需要使用像 sedtr 这样的工具来转换每一行输出。例如:

find /etc/passwd -print0 |
    xargs -0 egrep -Z 'root|www' |
    tr "\n" "\0" |
    xargs -0 -n1

此管道将使用 NUL 将文件名与 find 分开,然后将换行符转换为 egrep 返回的字符串中的 NUL。这会将 NUL 终止的字符串传递给管道中的下一个命令,在这种情况下,这只是 xargs 将输出转换回普通字符串,但它可以是任何你想要的。

【讨论】:

嗯,我不确定。我刚刚写了这个开关不相关(正如我在原始问题中提到的那样),因为手册页向我暗示,它仅在与生成文件名的开关结合使用时才相关(例如-l)。但是,一些基本测试并不那么清楚。需要更多调查。为过早的否决表示歉意,我无法撤消。 tr 解决方案非常适合所有没有类似 print0 选项的命令。 使用-0-z 开关的重点是文件名可能包含换行符。使用tr 等于根本不使用开关。 技术上,如果文件名中包含换行符(无论好坏都是合法的),这将失败。我从未见过这种情况发生,但这与人们对你(或我)大喊大叫解析ls 的原因相同 - 边缘情况。 我必须使用--null-data 而不是--null。我不是 100% 确定为什么,但从 grep --help 看来,--null-data 可能会改变 grep 的行为以使用空终止,而 --null 只会 输出 空终止 - 不会在处理 input 时将其考虑在内。【参考方案2】:

由于您已经在使用 GNU find,您可以使用其内部的正则表达式模式匹配功能来代替这些 grep,例如:

find <somebasedirectory> -regex ".*stringinfilenamesIwant.*" ! -regex ".*stringinfilesnamesIdont.*" -exec dosomecommand  + 

【讨论】:

【参考方案3】:

使用

find <somebasedirectory> -print0 | \
 grep -z stringinfilenamesIwant | \
 grep -zv stringinfilesnamesIdont | \
 xargs -0 dosomecommand

但是,该模式可能不包含换行符,请参阅bug report。

【讨论】:

【参考方案4】:

最新版本的 GNU grep 源现在可以使用 -z/--null 以空字符分隔输出,而以前只能与 -l 一起使用:

http://git.savannah.gnu.org/cgit/grep.git/commit/?id=cce2fd5520bba35cf9b264de2f1b6131304f19d2

这意味着您的问题在使用最新版本时会自动解决。

【讨论】:

【参考方案5】:

您可以将 find 的 -exec+ 终止符一起使用,而不是使用管道。要将多个命令链接在一起,您可以在 -exec 中生成一个 shell。

find ./ -type f -exec bash -c 'grep "$@" | grep -v something | xargs dosomething' --  +

【讨论】:

这会为 find 找到的每个文件生成一个新的 bash shell 吗?我无法从查找手册页中弄清楚... @AndrewFerrier - 不,+ 终止符使其功能类似于xargs。将生成一个 shell,并传入所有文件。这也适用于所有 POSIX 版本的 find,这与 print0 不同。【参考方案6】:
find <somebasedirectory> -print0 | xargs -0 -I % grep something '%'

【讨论】:

请提供您的答案的简要说明,以帮助 OP 和任何未来的访问者了解它为什么有效。 我不清楚这是如何解决这个问题的。这种风格不会在我原来的问题中调用dosomecommand

以上是关于find -print0 和 xargs -0 开关是不是有 grep 等效项?的主要内容,如果未能解决你的问题,请参考以下文章

xargs 和 exec详解

每天一个Linux命令(21)find命令_xargs参数

xargs和exec详解

find的使用及xargs

find与xargs配合使用

linux常用命令(19)find xargs