6

我有一个脚本,它调用 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 中的命令并将其传递给反引号以执行?

我尝试了各种方法将 Perl 代码嵌入到 shell 脚本变量中,但我似乎无法做到正确。

如何在 shell 脚本中正确引用 Perl 代码?

4

5 回答 5

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

基本上不需要引用。

于 2012-04-28T08:37:49.587 回答
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)直接进行所有计时。

于 2012-04-28T08:40:49.020 回答
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
于 2012-04-28T08:46:31.317 回答
1

以下是您的脚本的修改版本。您基本上需要了解某些应用程序的标准输出是针对标准错误(stderr),所以当您没有看到它们的输出放入变量时,您只需将其重定向到标准输出(stdout):

#/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
于 2012-04-28T09:19:40.437 回答
0

我认为 HiRes 时间的好处perl被一个相对繁重的外部过程所否定,并且它被单独调用两次。

如果您不需要那么多小数位的值。您可以使用 Bash 中的内置时间,例如

task() {
    [Some long running task ...]
}
TIMEFORMAT=%R
elapse=$({ time task > task.out 2>&1; } 2>&1)
echo $elapse
于 2012-05-08T08:27:25.367 回答