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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10361885/
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 02:08:51  来源:igfitidea点击:

How can I execute Perl code stored inside a shell script variable?

perlbashescaping

提问by GeneQ

I've got a script that calls Perl's Time::HiRes module to calculate elapsed time. Basically the script gets the time by passing the following one-liner:

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

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

to the Perl interpreter via back ticks and gets back the results.

通过回拨到 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

I tried to rewrite it in a more modular way but I'm stumped by the quoting rules of the bash shell.

我试图以更模块化的方式重写它,但我被 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 complains that something is not quoted properly. Why doesn't bash just expand the command in $NOW and pass it to the back tick to be executed?

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

I tried various ways to embed perl code in a shell script variable but can't seem to get it right.

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

Anyone knows how to quote perl code inside a shell script correctly?

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

回答by Mat

Using a function is the most straightforward way to do this, I think:

使用函数是最直接的方法,我认为:

#! /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

Essentially no quoting required.

基本上不需要引用。

回答by bdonlan

Your problem is that $NOWis just a string with some perl code in it. You need to tell bash to execute it, with backticks or $():

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

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

Also, bash can do arithmetic natively:

此外,bash 可以在本地进行算术运算:

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

No need to invoke bc.

无需调用bc.

Finally, starting and stopping perl is likely to take a lot of time, which will add noise to your results. I'd recommend running perl only once, and having perl itself execute the long running task. You'd then do all the computation within perl itself as well:

最后,启动和停止 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"

Or you could just use the bash builtin time(or /usr/bin/time) to just do all the timing directly.

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

回答by ikegami

If $NOWis outside of quotes, it gets split on whitespace.

如果$NOW在引号之外,它会在空白处拆分。

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

You can surround the variable by double-quotes to avoid this:

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

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

But you want to execute that string as a shell command. For that, use eval.

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

$ eval "$NOW"
1335602750.57325

Finally, to assign it, we use the backticks (or equivalent $( ... )).

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

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

The previously posted function is obviously cleaner, but you said you wanted help with quoting.

之前发布的函数显然更简洁,但你说你需要引用方面的帮助。



By the way,

顺便一提,

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

can be shortened to

可以缩短为

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

and even to the following (since the trailing new line is perfectly fine):

甚至到以下内容(因为尾随的新行非常好):

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

Or if you really wanted to golf:

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

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

回答by Khalil Khamlichi

below is a modified version of your script, you basically need to understand that some applications have thiere standard output towards stderr (standard error) so when you don't see thier output put in a variable you just need to redirect it to stdout (standard outputp)

下面是您的脚本的修改版本,您基本上需要了解某些应用程序具有面向 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

回答by pizza

I think the use the benefit of HiRes time is negated by the fact that perl is a relatively heavy external process and it is separately invoked two times. If you don't need that many decimal places for the value. you can use the time builtin in bash like

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

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