2012-04-28 15 views
6

Tengo un script que llama al módulo Perl's Time :: HiRes para calcular el tiempo transcurrido. Básicamente la secuencia de comandos obtiene la hora de pasar por el siguiente de una sola línea:¿Cómo puedo ejecutar el código de Perl almacenado dentro de una variable de script de shell?

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

al intérprete Perl a través de las garrapatas de la espalda y vuelve los resultados.

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

Traté de volver a escribir de una manera más modular, pero estoy perplejo por las normas de cotización de la cáscara del golpe.

#/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 se queja de que algo no se cita correctamente. ¿Por qué no bash simplemente expande el comando en $ NOW y lo pasa al último tic para ser ejecutado?

Intenté varias formas de incrustar código perl en una variable de script de shell pero parece que no puede obtenerlo derecha.

¿Alguien sabe cómo cotizar código perl dentro de un script de shell correctamente?

+0

@Mat Para que sea más fácil para las personas responder, acabo de mostrar un extracto de un script mucho más grande. Corté y pegué mal. Gracias por señalar. Fijo. Pero el problema sigue siendo la cita, sin embargo. – GeneQ

+1

FYI, puedes escribir tu perl one-liner como 'perl -MTime :: HiRes = time -e 'print time'' –

Respuesta

7

Utilizando una función es la forma más sencilla de hacer esto, pienso:

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

Esencialmente hay citando necesaria.

+0

¡Eso es todo! Tonto de mí. Habla de quedarte atrapado en una caja. A veces me olvido de que Bash es un lenguaje de programación completo. Gracias. – GeneQ

3

Tu problema es que $NOW es solo una cadena con algún código perl. Usted necesita decirle a bash para ejecutarlo, con acentos abiertos o $():

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

Además, fiesta puede hacer operaciones aritméticas de forma nativa:

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

No hay necesidad de invocar bc.

Por último, iniciar y detener Perl es probable que tome mucho tiempo, lo que agregará ruido a sus resultados. Recomiendo ejecutar Perl una sola vez, y tener Perl ejecutar la tarea de larga ejecución. A continuación, haría todo el cómputo dentro de Perl en sí, así:

#!/usr/bin/perl 

use Time::HiRes qw(time); 

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

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

O usted podría utilizar la fiesta builtin time (o /usr/bin/time) que acaba de hacer todo el tiempo directamente.

+0

Buen punto allí sobre el tiempo de carga del intérprete perl. Pero es un código viejo que estoy manteniendo así que no quiero cambiarlo demasiado. Cambiar a Perl sería portarlo, no refactorizar y eso es un poco demasiado trabajo. Prefiero escribirlo en Perl puro cualquier día. – GeneQ

+4

"no hay necesidad de invocar bc" - mi bash (y 'expr') no hace aritmética con números decimales. – Mat

+0

@Mat, ah, sí, zsh lo hizo, lo que me echó un poco de allí – bdonlan

1

Si $NOW está fuera de las comillas, se divide en espacios en blanco.

$ perl -E'say [email protected]; say for @ARGV' $NOW 
7 
perl 
-e 
'use 
Time::HiRes 
qw(time); 
print 
time' 

Puede rodear la variable de comillas dobles para evitar esto:

$ perl -E'say [email protected]; say for @ARGV' "$NOW" 
1 
perl -e 'use Time::HiRes qw(time); print time' 

pero que desea ejecutar esa cadena como un comando shell. Para eso, use eval.

$ eval "$NOW" 
1335602750.57325 

Por último, para asignarlo, utilizamos los acentos abiertos (o equivalente) $(...).

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

La función publicada anteriormente es obviamente más limpia, pero usted dijo que quería ayuda con las comillas.


Por cierto,

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

se puede acortar a

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

e incluso a la siguiente (ya que la nueva línea de salida está perfectamente bien):

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

O si realmente quieres gol f:

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

Agregué un poco al final para el color. – ikegami

0

a continuación es una versión modificada de su guión, que, básicamente, tiene que entender que algunas aplicaciones tienen Thiere salida estándar hacia stderr (error estándar) de modo que cuando no ves la salida emabrgo pone en una variable que sólo tiene que redirigir la salida estándar (outputp estándar)

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

Creo que el uso del beneficio del tiempo HiRes es negada por el hecho de que perl es un proceso externo relativamente pesado y que es invocado por separado dos veces. Si no necesita tantos lugares decimales para el valor. puede usar el tiempo incorporado en bash como

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