Establecer variables de entorno del archivo de pares clave / valor

514

TL; DR: ¿Cómo exporto un conjunto de pares clave / valor de un archivo de texto al entorno de shell?


Para el registro, a continuación se muestra la versión original de la pregunta, con ejemplos.

Estoy escribiendo un script en bash que analiza archivos con 3 variables en una carpeta determinada, esta es una de ellas:

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

Este archivo se almacena en ./conf/prac1

Mi script minientrega.sh luego analiza el archivo usando este código:

cat ./conf/$1 | while read line; do
    export $line
done

Pero cuando ejecuto minientrega.sh prac1en la línea de comando no establece las variables de entorno

También intenté usar source ./conf/$1pero el mismo problema aún se aplica

Quizás haya otra forma de hacerlo, solo necesito usar las variables de entorno del archivo que paso como argumento de mi script.

hugo19941994
fuente
Lo mismo en Unix: unix.stackexchange.com/questions/31797/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Lo mismo con Ruby: stackoverflow.com/questions/2139080/… , una joya que lo hace: github.com/bkeepers/dotenv
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Esta es una gran pregunta, pero está redactada de manera muy específica, con nombres de variables particulares ("MINIENTREGA_FECHALIMITE"? ¿Qué significa eso?) Y números (3). La pregunta general es simplemente: "¿Cómo exporto un conjunto de pares clave / valor de un archivo de texto al entorno de shell?".
Dan Dascalescu
Además, esto ya ha sido respondido en unix.SE y podría decirse que es más sobre el tema allí.
Dan Dascalescu

Respuestas:

209

Problema con el enfoque es el exportdel whilebucle está sucediendo en una cáscara de sub, y aquellos variable no estará disponible en el shell actual (shell padre del bucle while).

Agregar exportcomando en el archivo en sí:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"

Luego debe buscar el archivo en el shell actual usando:

. ./conf/prac1

O

source ./conf/prac1
anubhava
fuente
44
Aunque la lectura de la línea por línea del archivo y que pasa cada línea para exportno es ideal, el problema también puede ser fijado por el simple uso de redirección de entrada en el bucle: while read line; do ... ; done < ./conf/$1.
chepner
44
Y si no es de un archivo, use< <(commands that generate output)
o11c
55
Tienes una solución más limpia , tengo preferencia porset -o allexport
heralight
2
Si usa este archivo .env entre sistemas, la inserción exportlo rompería para cosas como Java, SystemD u otras herramientas
FilBot3
1
awk '{print "export " $0}' envfilecomando de conveniencia para anteponer la exportación al comienzo de cada línea
Shardj
888

Esto puede ser útil:

export $(cat .env | xargs) && rails c

La razón por la que uso esto es si quiero probar .envcosas en mi consola de rieles.

A gabrielf se le ocurrió una buena manera de mantener las variables locales. Esto resuelve el problema potencial al pasar de un proyecto a otro.

env $(cat .env | xargs) rails

He probado esto con bash 3.2.51(1)-release


Actualizar:

Para ignorar las líneas que comienzan con #, use esto (gracias al comentario de Pete ):

export $(grep -v '^#' .env | xargs)

Y si desea unsettodas las variables definidas en el archivo, use esto:

unset $(grep -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)

Actualizar:

Para manejar también valores con espacios, use:

export $(grep -v '^#' .env | xargs -d '\n')

en sistemas GNU - o:

export $(grep -v '^#' .env | xargs -0)

en sistemas BSD.

Silas Paul
fuente
66
Gracias, me gusta que esto no requiera anteponer nada al archivo; permite la compatibilidad con el formato Foreman (Procfile) .env.
natevw
29
Se me ocurrió la solución: carriles env $ (cat .env | xargs)
gabrielf
44
Esto parece no funcionar si alguno de los valores env tiene espacios, aunque en realidad no estoy seguro de cuál es la mejor / deseada forma de especificar valores con espacios. github.com/ddollar/foreman/issues/56 dice que debería funcionar así, export $(cat .env)pero no sé cómo lidiar con los espacios.
Dan Benamy
66
@BenjaminWheeler GNU Linux tiene -dpara configurar el delimitador, así que lo estoy intentando env $(cat .env | xargs -d '\n') rails, pero todavía errores con un archivo que no se encuentra si .envtiene espacios. ¿Alguna idea de por qué esto no funciona?
Bailey Parker
19
Aquí hay una variación más cortaeval $(cat .env) rails
manalang
413

-o allexportpermite exportar todas las siguientes definiciones de variables. +o allexportdeshabilita esta característica.

set -o allexport
source conf-file
set +o allexport
usuario4040650
fuente
99
¡Funciona de maravilla! Incluso si el .envarchivo tiene comentarios. ¡Gracias!
Slava Fomin II
99
Y en una líneaset -o allexport; source conf-file; set +o allexport
HarlemSquirrel
1
Esta es una excelente manera de leer en un archivo de propiedades, cuando el complemento Jenkins EnvInject no funciona. ¡Gracias!
Teresa Peters
21
@CMCDragonkai, para POSIX, lo sería set -a; . conf-file; set +a.
Charles Duffy
3
Este método funciona si las variables de entorno tienen espacios. Muchos de los otros no. Si bien el método eval () lo hace, también me pongo un poco extraño al usar eval
CommandZ el
139
set -a
. ./env.txt
set +a

Si env.txtes como:

VAR1=1
VAR2=2
VAR3=3
...

Explicaciones -a es equivalente a allexport . En otras palabras, cada asignación de variable en el shell se exporta al entorno (para ser utilizada por múltiples procesos secundarios). Se puede encontrar más información en la documentación integrada de Set :

-a     Cada variable o función que se crea o modifica recibe el atributo de exportación y se marca para exportar al entorno de los comandos posteriores.

El uso de '+' en lugar de '-' hace que estas opciones se desactiven. Las opciones también se pueden utilizar al invocar el shell. El conjunto actual de opciones se puede encontrar en $ -.

Dan Kowalczyk
fuente
¿Puedes explicar el -a y + a?
Otto
11
@Otto -aes equivalente a allexport. En otras palabras, cada asignación de variables en el shell se exportedita en el entorno (para ser utilizado por múltiples procesos secundarios). También vea este artículo gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
Dan Kowalczyk
33

La allexportopción se menciona en un par de otras respuestas aquí, para lo cual set -aes el acceso directo. Obtener el .env realmente es mejor que recorrer las líneas y exportar porque permite comentarios, líneas en blanco e incluso variables de entorno generadas por comandos. Mi .bashrc incluye lo siguiente:

# .env loading in the shell
dotenv () {
  set -a
  [ -f .env ] && . .env
  set +a
}

# Run dotenv on login
dotenv

# Run dotenv on every new directory
cd () {
  builtin cd $@
  dotenv
}
gsf
fuente
3
Esto se ve bien, pero ¿descarga las variables de entorno cuando abandona el directorio?
Bastian Venthur
1
No desarmo variables, y nunca ha sido un problema. Mis aplicaciones tienden a usar nombres de variables que son distintos, y si hay superposición, los pondré en blanco en ese .env con VAR=.
gsf
26
eval $(cat .env | sed 's/^/export /')
selvan
fuente
1
El uso le eval $(cat .env | sed 's/^[^$]/export /')permite tener líneas vacías para una mejor legibilidad.
Mario Uher
2
Me parece que cat .env | sed 's/^[^$]/export /'elimina el carácter inicial. Es decir, para un archivo A=foo\nB=bar\nque obtengo export =foo\nexport =bar\n. Esto funciona mejor para saltar líneas en blanco: cat .env | sed '/^$/! s/^/export /'.
Owen S.
(También noto por el bien de los golfistas de código UNIX que no necesita caten ninguno de los casos: eval $(sed 's/^/export /' .env)funciona igual de bien.)
Owen S.
21

Encontré que la forma más eficiente es:

export $(xargs < .env)

Explicación

Cuando tenemos un .envarchivo como este:

key=val
foo=bar

correr xargs < .envobtendrákey=val foo=bar

¡así que obtendremos un export key=val foo=bary es exactamente lo que necesitamos!

Limitación

  1. No maneja casos donde los valores tienen espacios en ellos. Los comandos como env producen este formato. - @Shardj
Huan
fuente
3
No maneja casos donde los valores tienen espacios en ellos. Comandos como envproducir este formato.
Shardj
19

Aquí hay otra sedsolución, que no ejecuta eval o requiere ruby:

source <(sed -E -n 's/[^#]+/export &/ p' ~/.env)

Esto agrega exportación, manteniendo comentarios en líneas que comienzan con un comentario.

Contenidos .env

A=1
#B=2

muestra de ejecución

$ sed -E -n 's/[^#]+/export &/ p' ~/.env
export A=1
#export B=2

Encontré esto especialmente útil al construir un archivo de este tipo para cargar en un archivo de unidad systemd, conEnvironmentFile .

tutuDajuju
fuente
no admite múltiples líneas en OSX
Abdennour TOUMI
17

He votado a favor de la respuesta del usuario 4040650 porque es simple y permite comentarios en el archivo (es decir, líneas que comienzan con #), lo cual es muy deseable para mí, ya que se pueden agregar comentarios que explican las variables. Solo reescribiendo en el contexto de la pregunta original.

Si el script se llama como se indica:, minientrega.sh prac1entonces minientrega.sh podría tener:

set -a # export all variables created next
source $1
set +a # stop exporting

# test that it works
echo "Ficheros: $MINIENTREGA_FICHEROS"

Lo siguiente se extrajo de la documentación establecida :

Esta construcción es tan complicada que merece su propia sección. set le permite cambiar los valores de las opciones de shell y establecer los parámetros posicionales, o mostrar los nombres y valores de las variables de shell.

set [--abefhkmnptuvxBCEHPT] [-o nombre-opción] [argumento ...] set [+ abefhkmnptuvxBCEHPT] [+ o nombre-opción] [argumento ...]

Si no se proporcionan opciones o argumentos, set muestra los nombres y valores de todas las variables y funciones de shell, ordenadas según la configuración regional actual, en un formato que puede reutilizarse como entrada para establecer o restablecer las variables establecidas actualmente. Las variables de solo lectura no se pueden restablecer. En el modo POSIX, solo se enumeran las variables de shell.

Cuando se proporcionan opciones, establecen o desarman los atributos del shell. Las opciones, si se especifican, tienen los siguientes significados:

-a Cada variable o función que se crea o modifica recibe el atributo de exportación y se marca para exportar al entorno de los comandos posteriores.

Y esto también:

El uso de '+' en lugar de '-' hace que estas opciones se desactiven. Las opciones también se pueden utilizar al invocar el shell. El conjunto actual de opciones se puede encontrar en $ -.

Nagev
fuente
14

Mejorando la respuesta de Silas Paul

exportar las variables en una subshell las convierte en locales para el comando.

(export $(cat .env | xargs) && rails c)

Jaydeep Solanki
fuente
Luego puede usar esto (set -a; source dev.env; set +a; rails c)para tener también los beneficios del abastecimiento (por ejemplo, el script puede ejecutarse).
wacha
12

SAVE=$(set +o | grep allexport) && set -o allexport && . .env; eval "$SAVE"

Esto guardará / restaurará sus opciones originales, cualesquiera que sean.

El uso set -o allexporttiene la ventaja de omitir correctamente los comentarios sin una expresión regular.

set +opor sí solo genera todas sus opciones actuales en un formato que bash puede ejecutar más tarde. También útil: set -opor sí solo, muestra todas sus opciones actuales en formato amigable para los humanos.

jwfearn
fuente
2
Probablemente exec env -i bashborraría el entorno existente antes de llamar evalsi necesita desarmar variables que solo se establecen dentro .env.
b4hand
11

El camino más corto que encontré:

Su .envarchivo:

VARIABLE_NAME="A_VALUE"

Entonces solo

. ./.env && echo ${VARIABLE_NAME}

Bonificación: debido a que es una línea corta, es muy útil en el package.jsonarchivo

  "scripts": {
    "echo:variable": ". ./.env && echo ${VARIABLE_NAME}"
  }
Flavien Volken
fuente
¿Qué tal si tienes muchas variables?
Madeo
@Madeo, puede agregar tantas líneas como desee, de la misma manera que la líneaVARIABLE_NAME="A_VALUE"
Flavien Volken
9

Más simple:

  1. agarrar el contenido del archivo
  2. elimine las líneas en blanco (solo en caso de que separe algunas cosas)
  3. eliminar cualquier comentario (solo en caso de que haya agregado algunos ...)
  4. agregar exporta todas las líneas
  5. eval toda la cosa

eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')

Otra opción (no tienes que correr eval(gracias a @Jaydeep)):

export $(cat .env | sed -e /^$/d -e /^#/d | xargs)

Por último, si quieres hacer tu vida REALMENTE fácil, agrega esto a tu ~/.bash_profile:

function source_envfile() { export $(cat $1 | sed -e /^$/d -e /^#/d | xargs); }

(¡ASEGÚRESE DE QUE VUELVA A CARGAR SU CONFIGURACIÓN DE BASH! source ~/.bash_profileO ... simplemente haga una nueva pestaña / ventana y resuelva el problema) lo llama así:source_envfile .env

Javier Buzzi
fuente
2
Tuve que leer el texto de la variable .env secreto gitlab para una tubería: Sobre la base de su solución de este comando funcionó para mí: source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "${2}" | tr -d '\r')) );. De alguna manera, gitlab guarda la variable secreta con un retorno de carro de Windows, así que tuve que recortar eso con tr -d '\r'.
metanerd
6

Puede usar su script original para configurar las variables, pero debe llamarlo de la siguiente manera (con un punto independiente):

. ./minientrega.sh

También puede haber un problema con el cat | while readenfoque. Yo recomendaría usar el enfoque while read line; do .... done < $FILE.

Aquí hay un ejemplo de trabajo:

> cat test.conf
VARIABLE_TMP1=some_value

> cat run_test.sh
#/bin/bash
while read line; do export "$line";
done < test.conf
echo "done"

> . ./run_test.sh
done

> echo $VARIABLE_TMP1
some_value
Extrapolador
fuente
A diferencia de la mayoría de las otras respuestas, esta solución no se evalúa test.confcomo un archivo de script. Eso lo hace mejor. Es más seguro no permitir secuencias de comandos a menos que realmente lo necesite, especialmente si alguien no se da cuenta de que eso está sucediendo (u olvida).
meustrus
5

A partir de otras respuestas, aquí hay una manera de exportar solo un subconjunto de líneas en un archivo, incluidos valores con espacios como PREFIX_ONE="a word":

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a
Victor Roetman
fuente
5

Aquí está mi variante:

  with_env() {
    (set -a && . ./.env && "$@")
  }

en comparación con las soluciones anteriores:

  • no pierde variables fuera del alcance (los valores de .envno están expuestos a la persona que llama)
  • no golpea las setopciones
  • devuelve el código de salida del comando ejecutado
  • utiliza posix compatible set -a
  • utiliza en .lugar de sourceevitar el bashism
  • el comando no se invoca si .envfalla la carga
with_env rails console
Elan Ruusamäe
fuente
También puede ejecutar en línea (las variables están expuestas a su sesión de terminal actual): set -a && . ./.env && "$@" && echo "your comand here"
Giovanne Afonso
4

Trabajo con docker-compose y .envarchivos en Mac, y quería importarlos .enva mi shell bash (para probar), y la "mejor" respuesta aquí fue tropezar con la siguiente variable:

.env

NODE_ARGS=--expose-gc --max_old_space_size=2048

Solución

Así que terminé usando evaly envolviendo mis definiciones de env env en comillas simples.

eval $(grep -v -e '^#' .env | xargs -I {} echo export \'{}\')

Versión Bash

$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
Nick Grealy
fuente
2

Tengo problemas con las soluciones sugeridas anteriormente:

  • La solución de @anubhava hace que escribir archivos de configuración amigables para bash sea muy molesto muy rápido y, además, es posible que no desee exportar siempre su configuración.
  • La solución @Silas Paul se interrumpe cuando tiene variables que tienen espacios u otros caracteres que funcionan bien en los valores entre comillas, pero $()hace un desastre.

Aquí está mi solución, que sigue siendo bastante terrible para la OMI, y no resuelve el problema de "exportar solo a un niño" abordado por Silas (aunque probablemente pueda ejecutarlo en un sub-shell para limitar el alcance):

source .conf-file
export $(cut -d= -f1 < .conf-file)
Guss
fuente
2

Mis requisitos fueron:

  • archivo .env simple sin exportprefijos (para compatibilidad con dotenv)
  • valores de apoyo entre comillas: TEXT = "alpha bravo charlie"
  • comentarios de apoyo con el prefijo # y líneas vacías
  • universal para mac / BSD y linux / GNU

Versión de trabajo completa compilada de las respuestas anteriores:

  set -o allexport
  eval $(grep -v '^#' .env | sed 's/^/export /')
  set +o allexport
Alex
fuente
1
¿Cuál es el punto de "-o allexport" si los antepone con "exportar"?
il - ya
2

Primero, cree un archivo de entorno que tenga todos los pares clave-valor de los entornos como a continuación y asígnele el nombre que desee en mi caso, es env_var.env

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

Luego cree un script que exportará todas las variables de entorno para el entorno de Python como se muestra a continuación y asígnele el nombre export_env.sh

#!/usr/bin/env bash

ENV_FILE="$1"
CMD=${@:2}

set -o allexport
source $ENV_FILE
set +o allexport

$CMD

Este script tomará el primer argumento como el archivo de entorno y luego exportará todas las variables de entorno en ese archivo y luego ejecutará el comando después de eso.

USO:

./export_env.sh env_var.env python app.py
anand tripathi
fuente
1

Encontré este hilo cuando intentaba reutilizar Docker --env-files en un shell. Su formato no es compatible con bash, pero es simple: name=valuesin comillas ni sustituciones. También ignoran las líneas en blanco y los #comentarios.

No pude conseguir que sea compatible con posix, pero aquí hay uno que debería funcionar en shells tipo bash (probado en zsh en OSX 10.12.5 y bash en Ubuntu 14.04):

while read -r l; do export "$(sed 's/=.*$//' <<<$l)"="$(sed -E 's/^[^=]+=//' <<<$l)"; done < <(grep -E -v '^\s*(#|$)' your-env-file)

Que no va a manejar tres casos en el ejemplo de los documentos relacionados anteriormente:

  • bash: export: `123qwe=bar': not a valid identifier
  • bash: export: `org.spring.config=something': not a valid identifier
  • y no manejará la sintaxis passthrough (una simple FOO)
Bob Zoller
fuente
1

Espacios en blanco en el valor

Aquí hay muchas respuestas excelentes, pero encontré que a todas les falta soporte para el espacio en blanco en el valor:

DATABASE_CLIENT_HOST=host db-name db-user 0.0.0.0/0 md5

He encontrado 2 soluciones que funcionan con dichos valores con soporte para líneas vacías y comentarios.

Uno basado en sed y @ javier-buzzi responden :

source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x "&"/g' .env)

Y uno con línea de lectura en un bucle basado en la respuesta @ john1024

while read -r line; do declare -x "$line"; done < <(egrep -v "(^#|^\s|^$)" .env)

La clave aquí es usar declare -xy poner la línea entre comillas dobles. No sé por qué, pero cuando vuelves a formatear el código de bucle en varias líneas, no funcionará: no soy un programador de bash, simplemente engullí estos, todavía es mágico para mí :)

Janusz Skonieczny
fuente
1
Tuve que modificar la sedsolución para que funcione. Pero primero alguna explicación: -ees la abreviatura de --expression, que simplemente dice sedqué operaciones tomar. -e /^$/delimina las líneas vacías de la salida (no el archivo). -e /^#/delimina los comentarios bash (líneas que comienzan con #) de la salida. 's/.*/declare -x "&"/g'reemplaza (sustituye) las líneas restantes con declare -x "ENV_VAR="VALUE"". Cuando obtienes esto, al menos para mí, no funcionó. En cambio, tuve que usar source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x &/g' .env), para quitar el "envoltorio extra .
jcasner
No uso ENV_VAR="lorem ipsum", tengo ENV_VAR=lorem ipsum, sin comillas en el archivo .env. Ahora no estoy seguro de por qué, pero esto probablemente fue problemático en otras herramientas que analizan este archivo. Y en lugar de lorem ipsumterminar con "lorem ipsum"valor, con comillas. Gracias por las explicaciones :)
Janusz Skonieczny
1
Si fuera mi elección, tampoco lo usaría ENV_VAR="lorem ipsum". En mi caso de uso, mi proveedor de alojamiento genera este archivo en función de algunas opciones de configuración que he establecido e insertan las comillas dobles. Por lo tanto, me veo obligado a solucionarlo. Gracias por su ayuda aquí, ¡me ahorró mucho tiempo tratando de encontrar las sedopciones correctas !
jcasner
1

prueba algo como esto

for line in `cat your_env_file`; do if [[ $line != \#* ]];then export $line; fi;done
gaozhidf
fuente
1
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t

Cómo funciona

  1. Crear archivo temporal.
  2. Escriba todos los valores de las variables de entorno actuales en el archivo temporal.
  3. Habilite la exportación de todas las variables declaradas en el script de fuentes al entorno.
  4. Leer .envarchivo Todas las variables se exportarán al entorno actual.
  5. Inhabilite la exportación de todas las variables declaradas en el script de fuentes al entorno.
  6. Lea el contenido del archivo temporal. Cada línea tendría declare -x VAR="val"que exportaría cada una de las variables al entorno.
  7. Eliminar el archivo temporal.
  8. Desarme la variable que contiene el nombre del archivo temporal.

Caracteristicas

  • Conserva los valores de las variables ya establecidas en el entorno.
  • .env puede tener comentarios
  • .env puede tener líneas vacías
  • .envno requiere encabezado o pie de página especial como en las otras respuestas ( set -ay set +a)
  • .envno requiere tener exportpara cada valor
  • un trazador de líneas
Alex Skrypnyk
fuente
0

Mi archivo .env se ve así:

DATABASE_URI="postgres://sa:***@localhost:5432/my_db"
VARIABLE_1="SOME_VALUE"
VALIABLE_2="123456788"

Usando las formas de @henke , el valor exportado termina conteniendo comillas"

"postgres://sa:***@localhost:5432/my_db"
"SOME_VALUE"
"123456788"

Pero quiero que el valor exportado contenga solo:

postgres://sa:***@localhost:5432/my_db
SOME_VALUE
123456788

Para solucionarlo, edito el comando para eliminar las comillas:

export $(grep -v '^#' dev.env | tr --delete '"' | xargs -d '\n')
Ricardo Yáñez
fuente
0

Éste hace frente a espacios en el RHS y omite variables "extrañas" como las definiciones de módulos bash (con '()' en ellas):

echo "# source this to set env vars" > $bld_dir/.env
env | while read line; do
    lhs="${line%%=*}"
    rhs="${line#*=}"
    if [[ "$lhs" =~ ^[0-9A-Za-z_]+$ ]]; then
        echo "export $lhs=\"$rhs\"" >> $bld_dir/.env
    fi
done
PatB
fuente