Establecer variables de entorno variables en bash (u otro)

9

Quiero que mi script lea un archivo que contenga pares clave / valor de variables de entorno para establecer, y luego establecerlas.

Hasta ahora, tengo esto:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

Y quiero leer un archivo como este:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Solo necesito estas variables en el alcance durante la duración del script, por lo que no necesito resolver el problema de establecer una variable en el script para el alcance principal .

Según mis pruebas, creo que mi problema radica export $key="$val", así que creo que solo necesito un reemplazo para esa línea.

palswim
fuente
un poco fuera de tema, pero dado que me quedé atascado al intentar imitar su secuencia de comandos, estoy compartiendo ... al tomar una clave ... usar key=${kv%%=*}es mejor que key=${kv%=*}porque %% y ## difieren de% y # para saber si la parte eliminada es la parte removible más corta o más larga
pulkitsinghal

Respuestas:

10

export $key=$valdebería funcionar bien en bash. Sospecho que su problema es la tubería ( |). Todos los comandos en una tubería se ejecutan en una subshell. En su ejemplo, la instancia de basheso está ejecutando su script bifurcará 2 subcapas: una para cat $1y otra para el while read .... Las variables se asignan y exportan en la subshell que ejecuta el ciclo while, y luego todas ellas se desechan rápidamente cuando la subshell sale. Una forma de solucionar esto es no generar subcapas en absoluto. En lugar de un uso inútil de cat , intente la redirección en su lugar:

while read ...; do
   ...
done < "$1"

BashFAQ 24 explica esto con mucho mayor detalle.

Un enfoque alternativo es simplemente obtener el archivo, con exportaciones incluidas:

. <(sed '/^export/!s/^/export /' "$1")

El <( )es el proceso de sustitución.

Como una nota adicional de precaución, dado que está leyendo variables de entorno de un argumento, debe asegurarse de que el archivo de argumentos $1sea ​​una fuente confiable para que no pueda dañar su script.

jw013
fuente
Utilicé la redirección anteriormente; algo más no debe haber funcionado cuando cambié el script para usar cat. Pero, el script con redirección funciona ahora, ¡gracias!
palswim
¡El uso inútil del gato se considera dañino!
Scott
2

( jw013 ya ha dado una respuesta correcta , pero quiero señalar algunos problemas más).

El lado derecho de una tubería se ejecuta en su propio subshell en bash (así como la mayoría de los otros shells, con la excepción de ATT ksh y zsh). Entonces, está configurando las variables de entorno en el subshell que ejecuta el bucle, pero no en el proceso del shell principal.

Aquí hay una solución fácil que es reemplazar el uso inútil de catuna redirección:

while read …; do …; done <"$1"

En general, si necesita preprocesar la entrada, coloque el bucle y el resto del script (al menos la parte que necesita las variables cambiadas) dentro de un bloque.

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Tenga en cuenta que, en general, while read line; do …no itera sobre las líneas de entrada. Ver ¿Por qué se while IFS= readusa con tanta frecuencia, en lugar de IFS=; while read..? para una explicación El idioma correcto para iterar sobre las líneas de entrada es

while IFS= read -r line; do 

Aquí, la eliminación de espacios en blanco iniciales y finales no importa, ya que no debería haber ninguna entrada de muestra dada, pero es posible que deba -revitar la expansión de la barra invertida. Es posible que while read linede hecho sea una forma correcta de leer su entrada (pero sospecho que solo si los valores de las variables no contienen nuevas líneas). También es posible que su formato de entrada sea ambiguo o más complejo de analizar. Verifique qué sucede si el valor de una de las variables contiene un carácter de nueva línea, una comilla doble o una barra diagonal inversa.

Un punto en el que definitivamente está manipulando la entrada es cuando extrae el valor:

val=`echo ${kv#*=} | sed 's/^"\|"$//g'`

En primer lugar, es necesario utilizar comillas dobles alrededor de la sustitución de variables: echo "${kv#*=}"; de lo contrario, el shell realiza la división de palabras y la generación de nombre de archivo (es decir, globalización) en él. En segundo lugar, echono es una forma confiable de imprimir una cadena, porque algunas cadenas que comienzan con a -se tratan como opciones, y algunos shells realizan una expansión de barra invertida en el argumento. Una forma confiable y portátil de imprimir una cadena es printf %s "${kv#*=}". Además, si el valor abarca varias líneas, el programa sed actuará en cada una por separado, lo cual es incorrecto. Esto se puede arreglar, pero no hay un método más fácil, que es el uso de las instalaciones de manipulación de cadenas de la shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Suponiendo que su entrada se analiza correctamente con un plano read, aquí hay una manera más simple de analizarla, aprovechando la IFSconfiguración para dividir cada línea:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Gilles 'SO- deja de ser malvado'
fuente
0

Otra buena opción podría ser colocarlo exporten el archivo y obtenerlo:

export XAUTHLOCALHOSTNAME="localhost"
export DISPLAY=":0"
export XAUTHORITY="/tmp/some-XAuthority"

y entonces:

. "$1"
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
fuente
0

/ env

EXPORT=value
#NOTEXPORT=notvalue

guión

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Łukasz Kurowski
fuente