bash xargs 可以为每个参数执行一个子shell 命令吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29278466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can xargs execute a subshell command for each argument?
提问by adelphus
I have a command which is attempting to generate UUIDs for files:
我有一个试图为文件生成 UUID 的命令:
find -printf "%P\n"|sort|xargs -L 1 echo $(uuid)
But in the result, xargs
is only executing the $(uuid)
subshell once:
但在结果中,xargs
只执行$(uuid)
一次子shell:
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3
Is there a one-liner (i.e not a function) to get xargs
to execute a subshell command on each input?
是否有一个单行(即不是函数)来xargs
在每个输入上执行子shell命令?
回答by hek2mgl
This is because the $(uuid)
gets expanded in the current shell. You could explicitly call a shell:
这是因为$(uuid)
在当前 shell 中被扩展了。您可以显式调用 shell:
find -printf "%P\n"| sort | xargs -I '{}' bash -c 'echo $(uuid) {}'
Btw, I would use the following command:
顺便说一句,我会使用以下命令:
find -exec bash -c 'echo "$(uuid) ${1#./}"' -- '{}' \;
without xargs
.
没有xargs
.
回答by guido
With a for loop:
使用 for 循环:
for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i"; done
for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i"; done
Edit: another way to do this:
编辑:另一种方法:
find -printf "%Pfind . -printf "%P\n" | sort | while IFS= read -r f; do echo "$(uuid) $f"; done
" -exec uuid -v 4 \; | sort | awk -F'find . -printf "%P find . -exec printf '%s\t' {} \; -exec uuidgen \; |
awk -F '\t' '{ sub(/.+\//,"", ); print , }' | sort -k2
" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done
' '{ print " " }'
this outputs the filename followed by the uuid (no subshell required) for letting the sort to happen, then swaps the two columns separated by null.
这将输出文件名后跟 uuid(不需要子shell)以让排序发生,然后交换由空分隔的两列。
回答by mklement0
hek2mgl's answerexplains the problem well and his solution works well; this answer looks at performance.
hek2mgl 的回答很好地解释了这个问题,他的解决方案效果很好;这个答案着眼于性能。
The accepted answer is a tad slow, because it creates a bash
process for every input line.
接受的答案有点慢,因为它bash
为每个输入行创建了一个过程。
While xargs
is generally preferable to and faster than a shell-code loop, in this particular case the roles are reversed, because shell functionality is needed in each iteration.
虽然xargs
通常比 shell 代码循环更可取且速度更快,但在这种特殊情况下,角色是相反的,因为每次迭代都需要 shell 功能。
The following alternative solution uses a while
loop to process the input lines, and, on my machine, is about twice as fastas the xargs
solution.
以下替代解决方案使用while
循环来处理输入行,并且在我的机器上,其速度大约是该xargs
解决方案的两倍。
- Note the use of
while
rather thanfor
, becausefor
cannot robustly parse command output (in short: filenames with embedded whitespace would break the command - see http://mywiki.wooledge.org/DontReadLinesWithFor).
- 注意使用
while
而不是for
,因为for
不能稳健地解析命令输出(简而言之:带有嵌入空格的文件名会破坏命令 - 请参阅http://mywiki.wooledge.org/DontReadLinesWithFor)。
If you're concerned about filenames with embedded newlines (very rare) and use GNUutilities, you could use NUL bytes as separators:
如果您担心带有嵌入换行符(非常罕见)的文件名并使用GNU实用程序,您可以使用 NUL 字节作为分隔符:
##代码##Update: The fastestapproachis to not use a shell loop at all, as evidenced by ?????'s clever answer. See below for a portable version of his answer.
更新:最快的方法是根本不使用 shell 循环,正如????? 的聪明答案所证明的那样。有关他的答案的便携式版本,请参见下文。
Compatibility note:
兼容性说明:
The OP's find
command implies the use of GNUfind
(Linux), and uses features (-printf
) that may not work on other platforms.
OP 的find
命令暗示使用GNUfind
(Linux),并使用-printf
在其他平台上可能无法使用的功能 ( )。
Here's a portable version of ?????'s answerthat uses only POSIX-compliant features of find
(and awk
).
Note, however, that uuid
is not a POSIX utility; since Linux and BSD-like systems (including OSX) have a uuidgen
utility, the command uses that instead:
这是????? 答案的便携式版本,它仅使用find
(和awk
) 的POSIX 兼容功能。
但是请注意,这uuid
不是 POSIX 实用程序;由于 Linux 和类似 BSD 的系统(包括 OSX)有一个uuidgen
实用程序,命令使用它来代替: