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

时间:2015-03-26 12:29:05

标签: bash xargs

我有一个试图为文件生成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在每个输入上执行subshel​​l命令?

3 个答案:

答案 0 :(得分:13)

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

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

是的,我会使用以下命令:

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

没有xargs

答案 1 :(得分:3)

hek2mgl's answer解释问题并且他的解决方案运作良好;这个答案着眼于表现

接受的答案有点慢,因为它会为每个输入行创建一个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\0" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done

更新 最快方法根本不使用shell循环, ᴳᵁᴵᴰᴼ's clever answer就是证明即可。 请参阅下面的便携版本的答案。


兼容性说明:

OP的find命令意味着使用 GNU find(Linux),并使用可能在其他平台上不起作用的功能(-printf)。

以下是ᴳᵁᴵᴰᴼ's answer可移植版本,仅使用符合POSIX标准的find(和awk)功能。
但请注意,uuid不是POSIX实用程序;由于Linux和类似BSD的系统(包括OSX)具有uuidgen实用程序,因此命令使用该实用程序:

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

答案 2 :(得分:2)

使用for循环:

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

编辑:另一种方法:

find -printf "%P\0" -exec uuid -v 4 \; | sort | awk -F'\0' '{ print $2 " " $1}'

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