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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 12:39:22  来源:igfitidea点击:

Can xargs execute a subshell command for each argument?

bashxargs

提问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, xargsis 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 xargsto 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 "%P
find . -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 bashprocess for every input line.

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

While xargsis 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 whileloop to process the input lines, and, on my machine, is about twice as fastas the xargssolution.

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

##代码##

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 findcommand 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 uuidis not a POSIX utility; since Linux and BSD-like systems (including OSX) have a uuidgenutility, the command uses that instead:

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

##代码##