Как я могу выполнить код Perl, хранящийся внутри переменной сценария оболочки?

у меня есть скрипт, который вызывает модуль 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.

#/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 просто не расширить команда в $NOW и передать ее на задний ТИК для выполнения?

Я пробовал различные способы встраивания кода perl в переменную сценария оболочки, но не могу его получить право.

кто-нибудь знает, как правильно цитировать код perl внутри сценария оболочки?

5 ответов


использование функции-самый простой способ сделать это, я думаю:

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

по существу не требуется цитирование.


ваша проблема в том, что $NOW - это просто строка с некоторым кодом perl. Вы должны сказать bash, чтобы выполнить его, с помощью backticks или $():

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 builtin time (или /usr/bin/time), чтобы просто сделать все время напрямую.


если $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'

но вы хотите выполнить эту строку как команду оболочки. Для этого используйте eval.

$ eval "$NOW"
1335602750.57325

наконец, чтобы назначить его, мы используем backticks (или эквивалент $( ... )).

$ 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

ниже приведена измененная версия вашего скрипта, вам в основном нужно понять, что некоторые приложения имеют стандартный вывод thiere в сторону 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

Я думаю, что использование преимущества времени найма отрицается тем фактом, что perl является относительно тяжелым внешним процессом, и он отдельно вызывается два раза. Если вам не нужно так много знаков после запятой для значения. вы можете использовать время, встроенное в bash, как

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