如何执行存储在shell脚本变量中的Perl代码?

时间:2012-04-28 08:28:17

标签: perl bash escaping

我有一个脚本调用Perl的Time :: HiRes模块来计算经过的时间。 基本上,脚本通过传递以下单行来获得时间:

use Time::HiRes qw(time); print time

通过后退标记到Perl解释器并获取结果。

#/bin/sh

START_TIME=`perl -e 'use Time::HiRes qw(time); print time'`
END_TIME=`perl -e 'use Time::HiRes qw(time); print time'`
ELAPSED_TIME=$(echo "($END_TIME - $START_TIME)" | bc)
echo $ELAPSED_TIME

我尝试以更模块化的方式重写它,但是我被bash shell的引用规则所困扰。

#/bin/sh
CALCULATE='bc'
NOW="perl -e 'use Time::HiRes qw(time); print time'"
START_TIME=`$NOW`
[Some long running task ...]
ELAPSED_TIME=$(echo "($NOW - $START_TIME)" | $CALCULATE)
echo $ELAPSED_TIME

Bash抱怨没有正确引用某些内容。为什么bash只是在$ NOW中扩展命令并将其传递给后面的tick来执行?

我尝试了各种方法在shell脚本变量中嵌入perl代码,但似乎无法得到它 对。

任何人都知道如何正确引用shell脚本中的perl代码?

5 个答案:

答案 0 :(得分:7)

使用函数是最简单的方法,我认为:

#! /bin/bash

now() {
    perl -e 'use Time::HiRes qw(time); print time';
}

calc=bc
time1=$(now)
time2=$(now)
elapsed=$(echo $time2 - $time1 | $calc)
echo $elapsed $time1 $time2

基本上不需要引用。

答案 1 :(得分:3)

您的问题是$NOW只是一个包含一些perl代码的字符串。您需要告诉bash执行它,使用反引号或$()

ELAPSED_TIME=$(echo "($($NOW) - $START_TIME)" | $CALCULATE)

另外,bash可以本地运算:

ELAPSED_TIME=$(( $($NOW) - $START_TIME))

无需调用bc

最后,启动和停止perl可能需要花费大量时间,这会给结果增加噪音。我建议只运行一次perl,并让perl本身执行长时间运行的任务。然后你也可以在perl中完成所有的计算:

#!/usr/bin/perl

use Time::HiRes qw(time);

my $start = time;
system(@ARGV);
my $end = time;

print "Elapsed: ", ($end - $start), "\n"

或者您可以直接使用bash内置time(或/usr/bin/time)来完成所有时间。

答案 2 :(得分:1)

如果$NOW超出引号,则会在空格上拆分。

$ perl -E'say 0+@ARGV; say for @ARGV' $NOW
7
perl
-e
'use
Time::HiRes
qw(time);
print
time'

您可以用双引号括起变量以避免这种情况:

$ perl -E'say 0+@ARGV; say for @ARGV' "$NOW"
1
perl -e 'use Time::HiRes qw(time); print time'

但是您希望将该字符串作为shell命令执行。为此,请使用eval

$ eval "$NOW"
1335602750.57325

最后,为了分配它,我们使用反引号(或等效的$( ... ))。

$ START_TIME=$(eval "$NOW")
$ echo $START_TIME
1335602898.78472

以前发布的功能显然比较清晰,但是你说你想要引用帮助。


顺便说一下,

perl -e 'use Time::HiRes qw(time); print time'

可以缩短为

perl -MTime::HiRes=time -e'print time'

甚至以下(因为尾随新行完全正常):

perl -MTime::HiRes=time -E'say time'

或者如果你真的想打高尔夫球:

perl -MTime::HiRes=time -Esay+time

答案 3 :(得分:0)

下面是你的脚本的修改版本,你基本上需要了解一些应用程序有标准输出朝向stderr(标准错误)所以当你没有看到他们的输出放入变量时你只需要将它重定向到stdout(标准输出p)

#/bin/sh
CALCULATE='bc'
echo 'starting'
NOW=$(perl -e 'use Time::HiRes qw(time); print time' 2>&1)
sleep 3
echo 'ending'
END_TIME=$(perl -e 'use Time::HiRes qw(time); print time' 2>&1)
ELAPSED_TIME=$(echo "($NOW - $START_TIME)")
echo $ELAPSED_TIME

答案 4 :(得分:0)

我认为使用HiRes时间的好处是因为perl是一个相对繁重的外部进程而且它被单独调用了两次。 如果您不需要该值的那么多小数位。你可以使用bash中内置的时间,如

task() {
    [Some long running task ...]
}
TIMEFORMAT=%R
elapse=$({ time task > task.out 2>&1; } 2>&1)
echo $elapse