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 字符,方法与find 和xargs 相同。
-Z --null 输出一个零字节(ASCII NUL 字符)而不是通常跟在文件名后面的字符。例如,‘grep -lZ’在每个文件名之后输出一个零字节,而不是通常的换行符。此选项使输出明确,即使存在包含不寻常字符(如换行符)的文件名。此选项可与“find -print0”、“perl -0”、“sort -z”和“xargs -0”等命令一起使用,以处理任意文件名,甚至包含换行符的文件名。
使用来自 GNU Coreutils 的 tr
正如 OP 正确指出的那样,此标志在处理输入或输出的文件名时最有用。为了将 grep 输出实际转换为使用 NUL 字符作为行尾,您需要使用像 sed 或 tr 这样的工具来转换每一行输出。例如:
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 等效项?的主要内容,如果未能解决你的问题,请参考以下文章