TL;DR:如何从文本文件中导出一组键/值对到shell环境中?


为了记录在案,以下是问题的原始版本,并附有示例。

我在bash中写了一个脚本,它在某个文件夹中解析带有3个变量的文件,这是其中之一:

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

该文件的存放路径为。/conf/prac1

我的脚本minientrega.sh然后使用以下代码解析文件:

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

但是当我在命令行中执行minientrega.sh prac1时,它不会设置环境变量

我也尝试使用source ./conf/$1,但同样的问题仍然适用

也许还有其他方法可以做到这一点,我只需要使用我传递的文件的环境变量作为脚本的参数。


当前回答

t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t

它是如何工作的

Create temp file. Write all current environment variables values to the temp file. Enable exporting of all declared variables in the sources script to the environment. Read .env file. All variables will be exported into current environment. Disable exporting of all declared variables in the sources script to the environment. Read the contents of the temp file. Every line would have declare -x VAR="val" that would export each of the variables into environment. Remove temp file. Unset the variable holding temp file name.

特性

保留环境中已设置的变量的值 .env可以有注释 .env可以有空行 .env不像其他答案那样需要特殊的页眉或页脚(set -a和set +a) .env不需要对每个值都导出 一行程序

其他回答

不知道为什么,或者我错过了什么,但在经历了大部分答案和失败之后。我意识到通过这个。env文件:

MY_VAR="hello there!"
MY_OTHER_VAR=123

我可以简单地这样做:

source .env
echo $MY_VAR

输出:你好!

似乎在Ubuntu linux中工作得很好。

这是我的变种:

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

与之前方案相比:

它不会泄漏作用域外的变量(来自.env的值不会暴露给调用者) 不clobber设置选项 返回已执行命令的退出代码 使用posix兼容的set -a 用途。而不是源,避免害羞 如果.env加载失败,则不会调用该命令

with_env rails console

出口是答案。

交互式exercices

由于shell是交互式的,您可以尝试内联!

$ mkdir conf && printf 'MINIENTREGA_%s="%s"\n' FECHALIMITE 2011-03-31 FICHEROS \
    "informe.txt programa.c" DESTINO ./destino/entrega-prac1 >conf/prac1 

$ set -- prac1
$ while read -r line; do export $line; done <"conf/$1"
bash: export: `programa.c"': not a valid identifier
$ while read -r line; do LANG=C export "$line"; done <"conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
"informe.txt programa.c"

注意双引号!

源代码。

$ set -- prac1
$ . "conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
informe.txt programa.c

好的,现在是关于出口的问题

导出命令告诉shell将shell变量导出到环境…因此,在使用任何子进程(如ruby, python, perl甚至其他shell脚本)之前,您必须导出脚本变量。

清除先前的操作以进行进一步演示

$ declare +x MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ unset MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO

所以在交互式shell中,最简单的方法是运行另一个shell:

$ set -- prac1
$ . "conf/$1"
$ bash -c 'declare -p MINIENTREGA_FICHEROS'
bash: line 1: declare: MINIENTREGA_FICHEROS: not found

$ export MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ bash -c 'declare -p MINIENTREGA_FICHEROS'
declare -x MINIENTREGA_FICHEROS="informe.txt programa.c"

用于导出变量的示例外壳包装器

最小的包装,没有安全问题(当其他用户编辑脚本时要注意!!)。

#!/bin/sh

while IFS== read -r varname _;do
    case $varname in
         *[!A-Za-z0-9_]* | '' ) ;;
         * ) export $varname ;;
    esac
done <conf/$1
. conf/$1

busybox sh -c 'set | grep MINIENTREGA'

以prac1作为参数运行,应该产生:

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

在细

获取配置文件与声明变量是一样的。 导出你的变量是一个指令,让shell在全局环境中为任何子进程共享他的变量。

这两个操作可以任意顺序进行。唯一的要求是在尝试运行任何子流程之前完成这两个操作。

你甚至可以同时进行这两种操作,通过在配置文件中导出,例如:

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

对于那些使用ruby的人,我制作了一个名为dotenv_export的小型实用工具gem。

使用

Dotenv_export是一个小的实用程序命令,它读取。env文件,并使用ruby dotenv实现将其转换为导出语句。

# first install `dotenv_export`
gem install dotenv_export

然后,在你的.bash_profile中,或者任何你想要加载环境变量的shell环境中,执行以下命令:

eval "$(dotenv-export /path/to/.env)"

我在Mac上使用docker-compose和.env文件,并想将.env导入到我的bash shell中(用于测试),这里的“最佳”答案是在以下变量上出错:

.env

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

解决方案

所以我最终使用eval,并将env var defs包装在单引号中。

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

Bash的版本

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