关于bash:从key/pair值文件设置环境变量

Set environment variables from file of key/pair values

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

下面是问题的原始版本,并举例说明。

我正在用bash编写一个脚本,它解析某个文件夹中包含3个变量的文件,这是其中之一:

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

此文件存储在/conf/prac1中。

然后,脚本minenterga.sh使用以下代码解析文件:

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

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

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

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


这可能会有所帮助:

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

我之所以使用它,是因为我想在Rails控制台中测试.env东西。

加布里耶夫想出了一个很好的方法来保持变量的局部性。这解决了从一个项目到另一个项目的潜在问题。

1
env $(cat .env | xargs) rails

我用bash 3.2.51(1)-release测试过这个

更新:

若要忽略以#开头的行,请使用该行(感谢皮特的评论):

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

如果您想要unset文件中定义的所有变量,请使用:

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

更新:

要使用空格处理值,请使用:

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

在GNU系统上或:

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

在BSD系统上。


-o allexport允许导出以下所有变量定义。+o allexport禁用此功能。

1
2
3
set -o allexport
source conf-file
set +o allexport


您的方法的问题是while循环中的export发生在子shell中,并且这些变量在当前shell(while循环的父shell)中不可用。

在文件本身中添加export命令:

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

然后,您需要在当前shell的文件中使用以下方法进行源代码:

1
. ./conf/prac1

1
source ./conf/prac1


1
2
3
set -a
. ./env.txt
set +a

如果env.txt是:

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


这里还有几个答案提到了allexport选项,其中set -a是捷径。源代码.env确实比循环换行和导出要好,因为它允许注释、空行,甚至由命令生成的环境变量。my.bashrc包括以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .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
}


1
eval $(cat .env | sed 's/^/export /')


这是另一个sed解决方案,它不运行eval或不需要ruby:

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

这将添加导出,保留以注释开头的行的注释。

Env含量

1
2
A=1
#B=2

样品运行

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

我发现这在构建这样一个文件以便在SystemD单元文件中加载时特别有用,使用EnvironmentFile


我已经对用户4040650的答案投了赞成票,因为它既简单又允许在文件中添加注释(即以开头的行),这对我来说是非常可取的,因为可以添加解释变量的注释。只是在原始问题的上下文中重写。

如果按照指示调用脚本:minientrega.sh prac1,那么minentrega.sh可以具有:

1
2
3
4
5
6
set -a # export all variables created next
source $1
set +a # stop exporting

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

以下是从集合文档中提取的:

This builtin is so complicated that it deserves its own section. set
allows you to change the values of shell options and set the
positional parameters, or to display the names and values of shell
variables.

set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument …] set
[+abefhkmnptuvxBCEHPT] [+o option-name] [argument …]

If no options or arguments are supplied, set displays the names and values of all shell
variables and functions, sorted according to the current locale, in a
format that may be reused as input for setting or resetting the
currently-set variables. Read-only variables cannot be reset. In POSIX
mode, only shell variables are listed.

When options are supplied, they set or unset shell attributes.
Options, if specified, have the following meanings:

-a Each variable or function that is created or modified is given the export attribute and marked for export to the environment of
subsequent commands.

这也是:

Using ‘+’ rather than ‘-’ causes these options to be turned off. The
options can also be used upon invocation of the shell. The current set
of options may be found in $-.


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

这将保存/恢复您的原始选项,无论它们是什么。

使用set -o allexport有一个优点,即在没有regex的情况下正确地跳过注释。

set +o本身以bash稍后可以执行的格式输出所有当前选项。同样方便:set -o本身,以人性化的格式输出您当前的所有选项。


改进塞拉斯·保罗的回答

在子shell上导出变量使其成为命令的本地变量。

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


更简单:

  • 获取文件的内容
  • 删除任何空白行(只是为了防止你把一些东西分开)
  • 删除任何评论(只是在添加一些…)
  • export添加到所有行中
  • 整个事情
  • eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')

    另一种选择(不必运行eval(感谢@jaydeep)):

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

    最后,如果你想让你的生活变得轻松,把它加到你的~/.bash_profile中:

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

    (确保重新加载bash设置!!!!source ~/.bash_profile或..只需创建一个新的选项卡/窗口并解决问题)您这样称呼它:source_envfile .env


    在其他答案的基础上,这里有一种方法可以只导出文件中的一部分行,包括带有空格的值,如PREFIX_ONE="a word"

    1
    2
    3
    set -a
    . <(grep '^[ ]*PREFIX_' conf-file)
    set +a

    您可以使用原始脚本来设置变量,但需要按以下方式调用它(使用独立点):

    1
    . ./minientrega.sh

    另外,cat | while read方法可能存在问题。我建议使用while read line; do .... done < $FILE方法。

    下面是一个工作示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    > 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

    我的要求是:

    • 不带export前缀的简单.env文件(用于与dotenv兼容)
    • 引号中的支持值:text="Alpha Bravo Charlie"
    • 以和空行为前缀的支持注释
    • 适用于Mac/BSD和Linux/GNU的通用

    根据以上答案编制的完整工作版本:

    1
    2
    3
      set -o allexport
      eval $(grep -v '^#' .env | sed 's/^/export /')
      set +o allexport

    我对先前建议的解决方案有异议:

    • @Anubhava的解决方案使得编写对bash友好的配置文件非常恼人,而且-您可能不希望总是导出您的配置。
    • @Silas-Paul解决方案会在变量具有在引用值中工作良好的空格或其他字符时中断,但$()会使问题变得一团糟。

    这是我的解决方案,它在IMO中仍然相当糟糕,并且没有解决Silas解决的"只导出一个孩子"问题(尽管您可能可以在子shell中运行它来限制范围):

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

    值中有空格

    这里有很多很好的答案,但我发现它们都缺乏对价值空间的支持:

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

    我发现了两种解决方案,它们都支持空行和注释,具有这样的价值。

    一个基于sed和@javier buzzi的答案:

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

    根据@john1024答案,在一个循环中有一个读行

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

    这里的关键是使用declare -x并将行放入双引号中。我不知道为什么,但是当你把循环代码重新格式化为多行代码时,它就不起作用了——我不是bash程序员,我只是狼吞虎咽地把这些代码拼凑在一起,对我来说仍然很神奇:)


    我在尝试在shell中重用docker --env-file时遇到了这个问题。它们的格式不兼容bash,但很简单:name=value,不引用,不替换。他们也忽略了空行和#注释。

    我不能完全让它与posix兼容,但这里有一个可以在类似bash的shell中工作(在osx 10.12.5上的zsh和Ubuntu 14.04上的bash中测试):

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

    在示例中,它不会处理上面链接的文档中的三个案例:

    • bash: export: `123qwe=bar': not a valid identifier
    • bash: export: `org.spring.config=something': not a valid identifier
    • 它不会处理直通语法(一个裸的FOO)

    如果因为某个变量包含一个包含空格的值而出错,可以尝试将bash的EDOCX1(内部字段分隔符)重置为
    ,以便bash将cat .env结果解释为env可执行文件的参数列表。

    例子:

    1
    2
    IFS=$'
    '
    ; env $(cat .env) rails c

    参见:

    • http://tldp.org/ldp/abs/html/internalvariables.html ifsref
    • https://unix.stackexchange.com/a/196761

    我的.EnV:

    1
    2
    3
    4
    5
    6
    7
    8
    #!/bin/bash
    set -a # export all variables

    #comments as usual, this is a bash script
    USER=foo
    PASS=bar

    set +a #stop exporting variables

    调用:

    1
    source .env; echo $USER; echo $PASS

    参考https://unix.stackexchange.com/questions/79068/how-to-export-variables-that-are-set-all-at-once