我有一个试图为文件生成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
在每个输入上执行subshell命令?
答案 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
while
而不是for
,因为for
无法强大地解析命令输出(简而言之:带有嵌入空格的文件名会破坏命令 - 请参阅http://mywiki.wooledge.org/DontReadLinesWithFor )。如果你担心带有嵌入换行符的文件名(非常罕见)并使用 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分隔的两列。