xargs可以为每个参数执行一个subshel​​l命令吗?

ade*_*hus 13 bash xargs

我有一个试图为文件生成UUID的命令:

find -printf "%P\n"|sort|xargs -L 1 echo $(uuid)
Run Code Online (Sandbox Code Playgroud)

但在结果中,xargs只执行$(uuid)一次子shell:

8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3
Run Code Online (Sandbox Code Playgroud)

是否有一个单行(即不是函数)xargs来执行每个输入的子shell命令?

hek*_*mgl 17

这是因为$(uuid)在当前shell中扩展了.您可以显式调用shell:

find -printf "%P\n"| sort | xargs -I '{}' bash -c 'echo $(uuid) {}'
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我会使用以下命令:

find -exec bash -c 'echo "$(uuid) ${1#./}"' -- '{}' \;
Run Code Online (Sandbox Code Playgroud)

没有xargs.

  • 做得很好; 但不仅是`-n 1`是多余的,因为`-I`意味着逐行处理,`-n 1`实际上将被_any_空格分割,无论是否为内线.虽然`-L 1`确实执行逐行处理,但仍然对每一行应用分词,而`-I`将整行视为_single_参数. (2认同)

mkl*_*nt0 6

hek2mgl的回答很好地解释了问题,并且他的解决方案效果很好;这个答案着眼于性能

\n\n

接受的答案有点慢,因为它bash为每个输入行创建一个过程。

\n\n

虽然xargs通常比 shell 代码循环更可取并且更快,但在这种特殊情况下,角色相反,因为每次迭代都需要 shell 功能。

\n\n

以下替代解决方案使用while循环来处理输入行,在我的机器上,速度大约是该解决方案的两倍xargs

\n\n
find . -printf "%P\\n" | sort | while IFS= read -r f; do echo "$(uuid) $f"; done\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n

如果您担心带有嵌入换行符的文件名(非常罕见)并使用GNU实用程序,则可以使用 NUL 字节作为分隔符:

\n\n
find . -printf "%P\\0" | sort -z | while IFS= read -d \'\' -r f; do echo "$(uuid) $f"; done\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新最快的方法是根本不使用 shell 循环,如\xe1\xb4\xb3\xe1\xb5\x81\xe1\xb4\xb5\xe1\xb4\xb0\xe1\xb4\xbc\'所示聪明的答案。\n请参阅下面的答案的便携式版本。

\n\n
\n\n

兼容性说明:

\n\n

OP的find命令暗示使用GNU find(Linux),并使用特性(-printf可能无法在其他平台上工作的功能 ( )。

\n\n

这是\xe1\xb4\xb3\xe1\xb5\x81\xe1\xb4\xb5\xe1\xb4\xb0\xe1\xb4\xbc\ 的答案的便携式版本,仅使用find(和awk)。
\n但请注意,这uuid不是 POSIX 实用程序;它是一个实用程序。由于 Linux 和类似 BSD 的系统(包括 OSX)有一个uuidgen实用程序,因此该命令使用它:

\n\n
 find . -exec printf \'%s\\t\' {} \\; -exec uuidgen \\; | \n   awk -F \'\\t\' \'{ sub(/.+\\//,"", $1); print $2, $1 }\' | sort -k2\n
Run Code Online (Sandbox Code Playgroud)\n