find . -exec cmd {} +find . -print0 | xargs -0 cmd两者都是对 find 找到的文件运行命令的可靠方法。
哪个是首选?哪个更便携、可靠、高效、多功能,为什么?
Sté*_*las 27
没有明显的赢家。我的建议是使用:
find . -exec cmd {} +
Run Code Online (Sandbox Code Playgroud)
只要它就足够了,因为它更便携,使用更少的资源并且问题更少,并且具有以下之一:
xargs -r0 -other-options -a <(find ... -print0 | ...) cmd
Run Code Online (Sandbox Code Playgroud)
find . -print0 | ... | xargs -0 -other-options cmd
Run Code Online (Sandbox Code Playgroud)
当您需要其他工具的附加功能xargs或对其他工具的输出进行后处理
find时,并且您知道您所在的系统支持这些非标准选项,并且这些限制不适用和/或可以忽略。
find已经有-exec cmd {} ';',这种变体对每个文件运行一次调用
cmd,并且还充当条件谓词,因为它在 70 年代中期的 Unix V5 中使用当前接口重新实现,但这种-exec cmd {} +形式将多个文件传递给cmd,尽可能多,来得很晚。它由 David Korn 编写,并于 1988 年在 System V Release 4 中首次发布(请参阅 参考资料lynx news://news.gmane.io/gmane.comp.standards.posix.austin.general/2192),但直到 SVR4.2(1992 年)才记录下来。
它仅被添加到 POSIX 标准的 2001 版中,并且一些实现find后来才添加它(4.2.12,2005 年的 GNU
find,2002 年的 FreeBSD,2006 年的 NetBSD,2015 年的 busybox)
xargs它本身来自 70 年代末的 PWB Unix。它曾经(并且仍然)有一个非常糟糕的界面,具有奇怪和不必要的功能和限制,理解一种独特的引用形式(尽管与尚未幸存的 PWB Unix shell 所理解的相当接近)。虽然它的目的是处理 的输出find,但它不能可靠地做到这一点。
-01990 年, GNU 中添加了一个选项xargs,同时又添加了一个新-print0选项。
find可以肯定的是,GNUfind作者在添加该选项时并不知道 SysV -exec {} +。之后,一些
-0// --null/-z选项--zero逐渐添加到其他 GNU 实用程序中,以处理可以携带任意文件路径和更常见的任意 C 字符串或命令行参数的NUL 分隔交换格式。
-d允许xargs任何单字节记录定界符的选项,使其变得多余-0,因为它与后来添加到 GNU xargs 中的相同-d '\0'(在 2005 年末发布的 4.2.26 中),但迄今为止,据我所知,仍然只支持由 GNU 提供xargs。
没有这些-0或-d(和-r,见下文),xargs几乎无法使用(可靠)。
-print0此后, /已被添加到其他一些实现中,甚至在一些商业 SysV 派生的 Unix 上,例如 Solaris 11。 shell的内置命令-0也支持它。findbosh
它们不是标准的,但在 POSIX 标准的下一版本中可能会成为标准(以及实用程序-d ''的选项read) 。
-exec cmd {} +是标准的,现在相当便携。它的支持在 busybox 中仍然是可选的,因此您可能会遇到不可用的基于 Linux 的嵌入式系统。-ok cmd {} +在执行之前提示用户的变体既不cmd标准也不可移植(也不方便,因为命令行可能会变得很大)。
-print0/xargs -0不是标准的,但它现在常见于 BSD 和基于 Linux 的系统(包括 GNU、busybox' 和 toybox')的find/实现中。AIX和 HP/UX上xargs仍然不支持它。
在 GNU 系统之外,仍然很少找到支持 NUL 分隔记录的其他标准实用程序( sort、sed、cut等)的其他实现。awk
find可以-print0用 来标准实现-exec printf '%s\0' {} +,但是没有xargs -0or sort/ sed/ grep... -z 的标准等效项,更一般地说,NUL 不能由 POSIX 文本实用程序处理(一般也不能由文件路径处理,因为它们不能保证是文本)。
除某些 BSD 外,如果未找到通常不需要的文件,find . -print0 | xargs -0 cmd仍将在不带参数的情况下运行一次。cmdGNU 实现xargs添加了一个-r选项来避免这种情况,但它不像-0.
在 中find . -exec cmd {} +,cmd继承了find的 stdin,因此cmd如果该命令例如从终端启动,仍然能够与用户交互。
而在 中find . -print0 | xargs -r0 cmd,根据xargs
实现的不同,cmd的 stdin 将是 /dev/null (就像 GNU 一样xargs)或者更糟糕的是继承xargs' stdin,这里是来自 的管道find,所以如果它从它的 stdin 读取,它将造成严重破坏。通过 GNU 实现xargs,可以使用-a和进程替换来解决这个问题:
xargs -r0a <(find . -print0) cmd # Korn syntax
xargs -r0a <{find . -print0} cmd # rc syntax
xargs -r0a /dev/fd/3 3<(find . -print0) cmd # yash syntax
xargs -r0a (find . -print0|psub) cmd # fish syntax (not parallel though)
Run Code Online (Sandbox Code Playgroud)
但这样的便携性要差很多。
在可靠性方面, find . -print0 | xargs -0 cmd如果find崩溃或过早被终止(例如因为它已达到资源限制),则可能会产生严重的后果。这是因为find将其输出写入不保证以 NUL 分隔符结尾的块中(例如, with find /var/tmp -name '*.tmp',块可能以 结尾/var),并且仍然会为非分隔记录xargs提供参数。cmd例如,在我们的示例中,如果在输出以 . 结尾的块后被杀死,则可以使用 as 参数调用cmd(如rm -rf)。/varfind/var
这个问题不影响-exec cmd {} +。
使用 时find . -exec cmd {} +,退出状态反映find和cmd失败,而使用 时find | xargs,在大多数 shell 中您只能获得 的退出状态xargs,因此可能会错过并非所有文件都能找到的事实。许多 shell 都有一个pipefail命令来缓解这种情况,另请参阅 zsh$pipestatus或 bash $PIPESTATUS,它们最终提供了更大的灵活性。
(非标准)-execdir cmd -- {} +(注意不向文件名添加前缀的实现--所需的)变体可以解决.find./-exec cmd {} +xargsxargs
在性能方面,find | xargs意味着更多的工作(至少一个额外的进程,以及通过管道推送该数据),并且,因为其中一些最终是并行完成的(find并且xargs同时运行),所以最终可能会增加争用,因为两者find并cmd竞争 I/O 访问,因此可能会使用更多的总体资源。由于这种并行性,在某些情况下,它最终可能会更快地执行任务,因为find可以继续搜索更多文件,同时cmd忙于执行前一批的一些 CPU 密集型任务(至少直到管道和find内部输出)缓冲区都已满)。
使用find . -exec cmd {} +,可以更轻松地cmd中止整个搜索。例如,与:
find . -exec sh -c 'if some-condition; then kill -s PIPE "$PPID"; exit 1; fi' sh {} +
Run Code Online (Sandbox Code Playgroud)
使用find . -print0 | xargs -0 cmd,cmd可以执行exit 255abort xargs,但find之后不会退出,直到它尝试将下一个块写入管道。
这些观点的主要论点是,从一般意义上来说,它更通用、更灵活、更通用。
find的-print0输出是文件列表的可后处理表示形式,可以被任何东西使用,而不仅仅是xargs -0. 例如,您可以这样做:
find . -print0 |
grep -z foo |
sort -z
Run Code Online (Sandbox Code Playgroud)
并且仍然获得可后处理、过滤和排序的文件路径列表。
同样xargs -0可以用在那些 NUL 分隔的列表上,无论它们来自输出find还是其他任何内容,无论表示文件路径还是其他任何内容。
在这方面,find . -exec cmd {} +仅适用于狭窄的特殊用例(即使它是最常见的用例之一)。
对于xargs,您可以使用-n或-s选项来限制传递给 的参数数量cmd。对于 GNU xargs,另请参阅并行-P运行多个实例的选项,或某些 BSD 的选项,以允许在文件列表后添加额外参数。cmdxargs -0 -J {} mv {} /dest/
您可以将 的输出保存find . -print0到文件中并稍后使用 处理它(例如,仅在find完全成功的情况下)xargs -0 cmd < file,避免 的cmd输出干扰find结果,包括(使用 GNU xargs):
xargs -r0a =(find . -print0) cmd # zsh
xargs -r0a (find . -print0|psub -f) cmd # fish
Run Code Online (Sandbox Code Playgroud)
没有与xargs的exit 255特殊处理等效的东西-exec cmd {} +(尽管请参阅上面的 about kill "$PPID")。
使用find | xargs,您可以更轻松地在不同的语言环境或更普遍的不同环境(包括变量、限制、umask...)中find运行xargs cmd
例如,通常需要find在 C 语言环境中运行来解决非文本文件名的问题,但通常仍希望cmd在用户的语言环境中运行。
LC_ALL=C find . -exec cmd {} +
Run Code Online (Sandbox Code Playgroud)
在 C 语言环境中运行 和find。cmd和
LC_ALL=C find . -exec env -u LC_ALL cmd {} +
Run Code Online (Sandbox Code Playgroud)
首先是非标准的,但cmd如果LC_ALL事先定义,也可能无法恢复原始语言环境。
LC_ALL=C find . -print0 | xargs -r0 cmd
Run Code Online (Sandbox Code Playgroud)
find仅将语言环境更改为 C。
作为一个特殊情况:
find . -exec sudo cmd {} +
Run Code Online (Sandbox Code Playgroud)
通常无法避免 args+env 大小的限制,因为sudo设置SUDO_COMMAND环境变量最终会重复参数列表。
find . -print0 | sudo xargs -r0 cmd
Run Code Online (Sandbox Code Playgroud)
没有问题,因为$SUDO_COMMAND在这种情况下仅包含xargs -0 cmd(不要使用find . -print0 | xargs -r0 sudo cmd)。
也可以看看:
sudo find . -print0 | xargs -r0 cmd
Run Code Online (Sandbox Code Playgroud)
其中文件列表由 找到root,但cmd以原始用户身份运行。或者
find . -print0 | (USERNAME=some-user; xargs -r0 cmd)
Run Code Online (Sandbox Code Playgroud)
在像 zsh 这样的 shell 中,它内置支持更改 (e)uid、(e)gids。
find . -print0 | xargs -r0 -- "${cmd[@]}"
Run Code Online (Sandbox Code Playgroud)
$cmd无论数组包含什么,都可以工作,而
find . -exec "${cmd[@]}" {} +
Run Code Online (Sandbox Code Playgroud)
如果数组的任何元素$cmd是;或其中有连续元素{},则失败。+
| 归档时间: |
|
| 查看次数: |
1917 次 |
| 最近记录: |