Estoy tratando de averiguar cómo hacer que bash (¿forzar?) Expanda las variables en una cadena (que se cargó desde un archivo).
Tengo un archivo llamado "something.txt" con el contenido:
hello $FOO world
Luego corro
export FOO=42
echo $(cat something.txt)
esto devuelve:
hello $FOO world
No expandió $ FOO a pesar de que se estableció la variable. No puedo evaluar o obtener el archivo, ya que intentará ejecutarlo (no es ejecutable como es, solo quiero la cadena con las variables interpoladas).
¿Algunas ideas?
eval
.eval
y es importante conocer las implicaciones.Respuestas:
Me topé con lo que creo que es LA respuesta a esta pregunta: el
envsubst
comando.En caso de que aún no esté disponible en su distribución, está en el
Paquete GNU
gettext
.@Rockallite: escribí un pequeño script de envoltura para solucionar el problema '\ $'.
(Por cierto, hay una "característica" de envsubst, explicada en https://unix.stackexchange.com/a/294400/7088 para expandir solo algunas de las variables en la entrada, pero estoy de acuerdo en que escapar de las excepciones es mucho más conveniente.)
Aquí está mi guión:
#! /bin/bash ## -*-Shell-Script-*- CmdName=${0##*/} Usage="usage: $CmdName runs envsubst, but allows '\$' to keep variables from being expanded. With option -sl '\$' keeps the back-slash. Default is to replace '\$' with '$' " if [[ $1 = -h ]] ;then echo -e >&2 "$Usage" ; exit 1 ;fi if [[ $1 = -sl ]] ;then sl='\' ; shift ;fi sed 's/\\\$/\${EnVsUbDolR}/g' | EnVsUbDolR=$sl\$ envsubst "$@"
fuente
$
escape del signo de dólar ( ), por ejemplo,echo '\$USER' | envsubst
generará el nombre de usuario actual con una barra inclinada hacia atrás, no$USER
.brew link --force gettext
Muchas de las respuestas usando
eval
yecho
funcionan, pero se rompen en varias cosas, como múltiples líneas, intentar escapar de los metacaracteres de shell, escapes dentro de la plantilla que no están destinados a ser expandidos por bash, etc.Tuve el mismo problema y escribí esta función de shell, que por lo que puedo decir, maneja todo correctamente. Esto aún eliminará solo las nuevas líneas finales de la plantilla, debido a las reglas de sustitución de comandos de bash, pero nunca he encontrado que eso sea un problema siempre que todo lo demás permanezca intacto.
apply_shell_expansion() { declare file="$1" declare data=$(< "$file") declare delimiter="__apply_shell_expansion_delimiter__" declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter" eval "$command" }
Por ejemplo, puede usarlo así con un
parameters.cfg
que es realmente un script de shell que solo establece variables, ytemplate.txt
que es una plantilla que usa esas variables:. parameters.cfg printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
En la práctica, lo uso como una especie de sistema de plantilla ligero.
fuente
eval
dificultades habituales . Intente agregar; $(eval date)
al final de cualquier línea dentro del archivo de entrada y ejecutará eldate
comando.puedes probar
echo $(eval echo $(cat something.txt))
fuente
echo -e "$(eval "echo -e \"`<something.txt`\"")"
si necesita mantener nuevas líneas.template.txt
es texto. Esta operación es peligrosa porque ejecuta (evalúa) comandos si lo son.No desea imprimir cada línea, desea evaluarla para que Bash pueda realizar sustituciones de variables.
FOO=42 while read; do eval echo "$REPLY" done < something.txt
Consulte
help eval
o el manual de Bash para obtener más información.fuente
eval
es peligroso ; no solo se$varname
expande (como lo haría conenvsubst
), sino$(rm -rf ~)
también.Otro enfoque (que parece asqueroso, pero lo estoy poniendo aquí de todos modos):
Escriba el contenido de something.txt en un archivo temporal, con una declaración de eco envuelto alrededor de él:
something=$(cat something.txt) echo "echo \"" > temp.out echo "$something" >> temp.out echo "\"" >> temp.out
luego vuelva a generarlo en una variable:
RESULT=$(source temp.out)
y $ RESULT lo tendrá todo expandido. ¡Pero parece tan malo!
fuente
Si solo desea que se amplíen las referencias de variables (un objetivo que tenía para mí), puede hacer lo siguiente.
contents="$(cat something.txt)" echo $(eval echo \"$contents\")
(Las comillas de escape alrededor de $ contenidos son clave aquí)
fuente
eval "echo \"$$contents\""
y conservó el espacio en blancoSiguiente solución:
permite la sustitución de variables definidas
deja los marcadores de posición de variables sin cambios que no están definidos. Esto es especialmente útil durante las implementaciones automatizadas.
admite el reemplazo de variables en los siguientes formatos:
${var_NAME}
$var_NAME
informa qué variables no están definidas en el entorno y devuelve el código de error para tales casos
TARGET_FILE=someFile.txt; ERR_CNT=0; for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do VAR_VALUE=${!VARNAME}; VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' ); VAR_VALUE2=${!VARNAME2}; if [ "xxx" = "xxx$VAR_VALUE2" ]; then echo "$VARNAME is undefined "; ERR_CNT=$((ERR_CNT+1)); else echo "replacing $VARNAME with $VAR_VALUE2" ; sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; fi done if [ ${ERR_CNT} -gt 0 ]; then echo "Found $ERR_CNT undefined environment variables"; exit 1 fi
fuente
Si algo.txt tiene solo una línea, un
bash
método (una versión más corta de la respuesta "asquerosa" de Michael Neale ), usando la sustitución de proceso y comando :FOO=42 . <(echo -e echo $(<something.txt))
Salida:
Tenga en cuenta que
export
no es necesario.Si algo.txt tiene una o más líneas, un método de valoración de GNU :
sed
e
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
fuente
sed
evaluación que mejor se ajusta a mi propósito. Necesitaba producir scripts a partir de plantillas, que tendrían valores llenados de variables exportadas en otro archivo de configuración. Maneja correctamente el escape para la expansión de comandos, por ejemplo, poner\$(date)
la plantilla para obtener$(date)
la salida. ¡Gracias!$ eval echo $(cat something.txt) hello 42 world $ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17) Copyright (C) 2007 Free Software Foundation, Inc.
fuente
foo=45 file=something.txt # in a file is written: Hello $foo world! eval echo $(cat $file)
fuente
envsubst
es una gran solución (consulte la respuesta de LenW) si el contenido que está sustituyendo tiene una longitud "razonable".En mi caso, necesitaba sustituir el contenido de un archivo para reemplazar el nombre de la variable.
envsubst
requiere que el contenido se exporte como variables de entorno y bash tiene un problema al exportar variables de entorno que son más de un megabyte aproximadamente.solución awk
Usando la solución de cuonglm de una pregunta diferente:
needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.) needle_file="doc1_base64.txt" # Will be substituted for the needle haystack=$requestfile1 # File containing the needle out=$requestfile2 awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out
Esta solución funciona incluso para archivos grandes.
fuente
Los siguientes trabajos:
bash -c "echo \"$(cat something.txt)"\"
fuente
$foo
, sino también inseguras como$(rm -rf ~)
.