2012-11-13 33 views
7

bash剧本我想分配内建一个数组变量times的输出,但我发现没有更好的办法比击:分配的“次”内置输出到一个变量

tempnam=/tmp/aaa_$$_$RANDOM 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 

我将输出写入临时文件并将其读回到times_a数组中,因为pipeline或$(times)将在子外壳中执行并返回错误的值。

没有临时文件的任何更好的解决方案?

回答

6

您需要解决的根本问题是如何在不使用临时文件的情况下同时执行time和变量赋值。几乎每种方法Bash都提供了将一个东西的输出管道输出到另一个东西,或者捕获一个命令的输出,其中一方在子外壳中工作。

这里是你可以不用临时文件的一种方法,但我会提醒你,这不是很漂亮,这是无法移植到其他炮弹,它至少需要击4:

coproc co { cat; }; times 1>&${co[1]}; eval "exec ${co[1]}>&-"; mapfile -tu ${co[0]} times_a 

我会为你解决这个问题:

coproc co { cat; } 

这创建了一个协处理器;这是一个在后台运行的进程,但您可以通过管道与标准输入和标准输出进行通信,这些FD是${co[0]}(标准为cat)和${co[1]}(标准为cat)。这些命令在一个子shell中执行,所以我们不能在那里执行任何一个目标(运行times或读入一个变量),但我们可以使用cat来简单地将输入传递给输出,然后使用该管道在当前shell中与timesmapfile交谈。

times >&${co[1]}; 

运行times,其重定向标准输出到标准的cat命令。

eval "exec ${co[1]}>&-" 

关闭cat命令的输入结束。如果我们不这样做,cat将继续等待输入,保持其输出打开,并且mapfile将继续等待输入,导致您的shell挂起。 exec,当不传递任何命令时,只需将其重定向应用到当前shell;重定向到-关闭FD。我们需要使用eval,因为Bash似乎遇到了exec ${co[1]}>&-的问题,将FD解释为命令而不是重定向的一部分;使用eval允许首先替换该变量,然后执行。

mapfile -tu ${co[0]} times_a 

最后我们实际上从协处理器中读取标准数据。我们设法在这个shell中运行了timesmapfile命令,并且没有使用临时文件,尽管我们在两个命令之间使用了临时进程作为管道。

请注意,这有一个微妙的比赛。如果你逐个执行这些命令,而不是全部作为一个命令,则最后一个命令失败;因为当你关闭cat的标准时,它退出,导致协处理器退出并且FD关闭。看起来,当全部在一行上执行时,mapfile被执行得足够快,以至于协处理在运行时仍然打开,因此它可以从管道读取;但我可能会很幸运。我还没有想出解决这个问题的好方法。

总而言之,只需写出临时文件就简单多了。我会用mktemp生成一个文件名,如果你在一个脚本的时候,添加陷阱,以保证在退出之前,你清理临时文件:

tempnam=$(mktemp) 
trap "rm '$tempnam'" EXIT 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 
+1

我希望我能更给予好评这一点,我已经学会了很多读这个,真的很好的答案! – higuaro

0

一种可能性做同样的事情会

times > >(other_command) 

这是一个过程替代combided输出重定向。这种方式times在当前shell中执行,输出重定向到一个新的子进程。因此,使用mapfile做这件事并没有多大意义,因为该命令不会在同一个shell中执行,而且这可能不是您想要的。这种情况有点棘手,因为你不能调用在子shell中执行的shell内置函数。

1

哇,好问题。

一个改进就是使用mktemp,这样你就不会依赖随机性来保持文件的独特性。

TMPFILE=$(mktemp aaa_XXXXXXXXXX) 
times > "$TMPFILE" 
mapfile -t times_a < ${tempnam} 
rm "$TMPFILE" 

此外,我使用替代mapfile(因为我没有mapfile)。

a=0; for var in $(cat "$TMPFILE"); do ((a++)); TIMES_A[$a]=$var; done 

但是,是啊,我不明白你怎么能没有文件或命名管道。