Quería inicializar algunas cadenas en la parte superior de mi script con variables que aún no se han establecido, como:
str1='I went to ${PLACE} and saw ${EVENT}'
str2='If you do ${ACTION} you will ${RESULT}'
y más tarde PLACE
, EVENT
, ACTION
, y RESULT
se establecerá. Quiero poder imprimir mis cadenas con las variables expandidas. Es mi única opcióneval
? Esto parece funcionar:
eval "echo ${str1}"
es esta norma? ¿Hay una mejor manera de hacer esto? Sería bueno no ejecutar eval
teniendo en cuenta que las variables podrían ser cualquier cosa.
A medida que entiendo su significado, no creo que ninguna de estas respuestas sea correcta.
eval
no es necesario de ninguna manera, ni tiene ninguna necesidad incluso de evaluar dos veces sus variables.Es cierto, @Gilles se acerca mucho, pero no aborda el problema de posiblemente anular los valores y cómo deberían usarse si los necesita más de una vez. Después de todo, una plantilla debe usarse más de una vez, ¿verdad?
Creo que es más importante el orden en el que los evalúas. Considera lo siguiente:
PARTE SUPERIOR
Aquí establecerá algunos valores predeterminados y se preparará para imprimirlos cuando se llame ...
MEDIO
Aquí es donde define otras funciones para llamar a su función de impresión en función de sus resultados ...
FONDO
Ya lo tiene todo configurado, así que aquí es donde ejecutará y extraerá sus resultados.
RESULTADOS
Analizaré por qué en un momento, pero ejecutar lo anterior produce los siguientes resultados:
CÓMO FUNCIONA:
La característica clave aquí es el concepto de
conditional ${parameter} expansion.
Puede establecer una variable en un valor solo si no está establecida o es nula utilizando el formulario:Si, en cambio, desea establecer solo una variable no establecida, omitirá los
:colon
valores nulos y permanecerá como está.EN ALCANCE:
Puede notar eso en el ejemplo anterior
$PLACE
y$RESULT
cambiarlo cuando se configura a través deparameter expansion
aunque_top_of_script_pr()
ya se haya llamado, presumiblemente configurándolos cuando se ejecuta. La razón por la que esto funciona es que_top_of_script_pr()
es una( subshelled )
función: la incluí enparens
lugar de la{ curly braces }
utilizada para los demás. Debido a que se llama en una subshell, cada variable que establece eslocally scoped
y cuando regresa a su shell principal esos valores desaparecen.Pero cuando se
_more_important_function()
establece$ACTION
esglobally scoped
así, afecta a la_less_important_function()'s
segunda evaluación de$ACTION
porque_less_important_function()
establece$ACTION
solo a través de${parameter:=expansion}.
:NULO
¿Y por qué utilizo el
:colon?
Pozo principal? Laman
página te dirá que: does nothing, gracefully.
, como ves,parameter expansion
es exactamente lo que suena,expands
al valor de${parameter}.
So, cuando establecemos una variable con,${parameter:=expansion}
nos queda su valor, que el shell Intente ejecutar en línea. Si intentara ejecutarsethe cemetery
, solo te escupiría algunos errores.PLACE="${PLACE:="the cemetery"}"
produciría los mismos resultados, pero también es redundante en este caso y preferí que el shell: ${did:=nothing, gracefully}.
Te permite hacer esto:
AQUÍ-DOCUMENTOS
Y, por cierto, la definición en línea de una variable nula o sin definir también es la razón por la que funciona lo siguiente:
La mejor manera de pensar en un
here-document
es como un archivo real transmitido a un descriptor de archivo de entrada. Más o menos eso es lo que son, pero diferentes shells los implementan de manera ligeramente diferente.En cualquier caso, si no cita el
<<LIMITER
flujo, lo transfiere y lo evalúa. Por loexpansion.
tanto, declarar una variable en unhere-document
puede funcionar, pero solo a través de loexpansion
cual lo limita a establecer solo variables que aún no están establecidas. Aún así, eso se adapta perfectamente a sus necesidades tal como las describió, ya que sus valores predeterminados siempre se establecerán cuando llame a la función de impresión de su plantilla.POR QUÉ NO
eval?
Bueno, el ejemplo que he presentado proporciona un medio seguro y efectivo de aceptar.
parameters.
Debido a que maneja el alcance, cada variable dentro de set via${parameter:=expansion}
se puede definir desde afuera. Entonces, si pones todo esto en un script llamado template_pr.sh y ejecutas:Obtendrías:
Esto no funcionaría para aquellas variables que se configuraron literalmente en el script, como
$EVENT, $ACTION,
y$one,
pero solo las definí de esa manera para demostrar la diferencia.En cualquier caso, la aceptación de una entrada desconocida en una
evaled
declaración es inherentemente insegura, mientras queparameter expansion
está específicamente diseñada para hacerlo.fuente
Puede usar marcadores de posición para plantillas de cadena en lugar de variables sin expandir. Esto se volverá complicado bastante rápido. Si lo que está haciendo es una plantilla muy pesada, es posible que desee considerar un idioma con una biblioteca de plantillas real.
La desventaja de lo anterior es que la variable de plantilla debe ser su propia palabra (por ejemplo, no se puede hacer
"%prefix%foo"
). Esto podría solucionarse con algunas modificaciones, o simplemente codificando la variable de plantilla en lugar de que sea dinámica.fuente