Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

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

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

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

#/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 backtick to be executed?

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

How can I quote Perl code inside a shell script correctly?

like image 519
GeneQ Avatar asked Apr 28 '12 08:04

GeneQ


Video Answer


2 Answers

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 is required.

like image 106
Mat Avatar answered Sep 29 '22 09:09

Mat


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

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

Also, Bash can do arithmetic natively:

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

There isn't any need to invoke 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:

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

like image 35
bdonlan Avatar answered Sep 29 '22 09:09

bdonlan