xargs 可以为每个参数执行一个子 shell 命令吗?

Can xargs execute a subshell command for each argument?

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

find -printf "%P\n"|sort|xargs -L 1 echo $(uuid)

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

8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3

是否有单行代码(即不是函数)让 xargs 在每个输入上执行子 shell 命令?

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

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

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

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

没有xargs

使用for循环:

for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i";  done

编辑:另一种方法:

find -printf "%P[=11=]" -exec uuid -v 4 \; | sort | awk -F'[=11=]' '{ print  " " }'

这会输出文件名,后跟 uuid(不需要子 shell)以便进行排序,然后交换由 null 分隔的两列。

很好地解释了问题,他的解决方案也很有效;这个答案着眼于性能

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

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

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

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

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

find . -printf "%P[=11=]" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done

更新最快方法是不使用shell循环,如 所证明的那样。 请参阅下面的便携版他的回答。


兼容性说明:

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

这是 便携式版本,它仅使用 find(和 awk)的 POSIX 兼容功能.
但是请注意,uuid 不是 POSIX 实用程序;由于 Linux 和类似 BSD 的系统(包括 OSX)有一个 uuidgen 实用程序,该命令改为使用它:

 find . -exec printf '%s\t' {} \; -exec uuidgen \; | 
   awk -F '\t' '{ sub(/.+\//,"", ); print ,  }' | sort -k2