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
- 请注意使用
while
而不是 for
,因为 for
无法可靠地解析命令输出(简而言之:带有嵌入空格的文件名会破坏命令 - 请参阅 http://mywiki.wooledge.org/DontReadLinesWithFor).
如果您担心带有嵌入式换行符的文件名(非常罕见)并使用 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
我有一个试图为文件生成 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
- 请注意使用
while
而不是for
,因为for
无法可靠地解析命令输出(简而言之:带有嵌入空格的文件名会破坏命令 - 请参阅 http://mywiki.wooledge.org/DontReadLinesWithFor).
如果您担心带有嵌入式换行符的文件名(非常罕见)并使用 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