2010-05-24 15 views
10

¿Hay forma más elegante de hacer la evaluación perezosa de los siguientes:Lazy evaluación en Bash

 
pattern='$x and $y' 
x=1 
y=2 
eval "echo $pattern" 

resultados:

 
1 and 2 

Funciona, pero eval "echo ..." sólo se siente descuidado y puede ser insegura de alguna manera . ¿Hay una mejor manera de hacer esto en Bash?

+0

tengo curiosidad por qué quiere hacer esto o lo que en realidad estás tratando de lograr. Algunas veces, 'eval' es el correcto o el único camino a seguir, pero también hay características especiales de' declare' y 'printf' que pueden ser útiles. Y puede haber otras formas de lograr lo que busca. –

+0

Tengo un script bash que quiero que sea configurable. Quiero que el usuario tenga la capacidad de especificar un "patrón". Más tarde, algunas variables en el patrón serán reemplazadas por actividades ejecutadas por el script (consultas SQL, llamadas SOAP y otras utilidades internas) y pasadas a otro programa de línea de comandos. Soy algo nuevo para Bash y algo acerca de este enfoque simplemente se siente mal. Gracias por preguntar más detalles. – User1

Respuesta

4

Siempre que los valores no provengan de fuentes no confiables, no existe riesgo de seguridad. Hay algunas alternativas, sin embargo, puede utilizar el comando de envsubst gettext, por ejemplo:

$ pattern='$x and $y' 
$ x=1 y=2 envsubst <<< $pattern 
1 and 2 
+0

Creo que las cosas de seguridad son un poco paranoicas en este caso. Me gustan las citas extra. – User1

0

Tiene razón, eval es un riesgo de seguridad en este caso. Este es un enfoque posible:

pattern='The $a is $b when the $z is $x $c $g.' # simulated input from user (use "read") 
unset results 
for word in $pattern 
do 
    case $word in 
     \$a) 
      results+=($(some_command)) # add output of some_command to array (output is "werewolf" 
      ;; 
     \$b) 
      results+=($(echo "active")) 
      ;; 
     \$c) 
      results+=($(echo "and")) 
      ;; 
     \$g) 
      results+=($(echo "the sky is clear")) 
      ;; 
     \$x) 
      results+=($(echo "full")) 
      ;; 
     \$z) 
      results+=($(echo "moon")) 
      ;; 
      *) 
      do_something # count the non-vars, do a no-op, twiddle thumbs 
      # perhaps even sanitize %placeholders, terminal control characters, other unwanted stuff that the user might try to slip in 
      ;; 
    esac 
done 
pattern=${pattern//\$[abcgxz]/%s} # replace the vars with printf string placeholders 
printf "$pattern\n" "${results[@]}" # output the values of the vars using the pattern 
printf -v sentence "$pattern\n" "${results[@]}" # put it into a variable called "sentence" instead of actually printing it 

la salida sería "El hombre lobo está activo cuando la luna está llena y el cielo está despejado." El mismo programa, si el patrón es 'The $ x $ z out $ c $ g, entonces $ a debe ser $ b.' entonces la salida sería "La luna llena está afuera y el cielo está despejado, por lo que el hombre lobo debe estar activo".

+1

La respuesta coincide con el ejemplo de la pregunta. Pero no parece ser muy útil para algo más complicado que los reemplazos de cuerdas básicos. Si trato de seguir este enfoque, tengo que escribir un intérprete Bash en Bash. – ceving

+0

Mi respuesta es una forma de aislar el script de la entrada no confiable de un usuario. Se basa en el comentario del OP adjunto a la pregunta en respuesta a mi consulta. –

2

Una posibilidad es seguro de usar una función:

expand_pattern() { 
    pattern="$x and $y" 
} 

eso es todo. A continuación, utilice la siguiente manera:

x=1 y=1 
expand_pattern 
echo "$pattern" 

Incluso puede utilizar x y y como variables de entorno (para que no se encuentran en el alcance principal):

x=1 y=1 expand_pattern 
echo "$pattern" 
+1

Esta es la respuesta que iba a escribir si no hubiera estado aquí. (Por otra parte, solo detecté esta pregunta en virtud de que se había agregado la respuesta, volviendo a la página principal). :) –