从密钥/对值文件中设置环境变量

hug*_*994 425 variables bash environment-variables

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


为了记录,下面是问题的原始版本,带有示例.

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

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"
Run Code Online (Sandbox Code Playgroud)

该文件存储在./conf/prac1中

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

cat ./conf/$1 | while read line; do
    export $line
done
Run Code Online (Sandbox Code Playgroud)

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

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

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

Sil*_*aul 765

这可能会有所帮助:

export $(cat .env | xargs) && rails c
Run Code Online (Sandbox Code Playgroud)

我使用它的原因是我想.env在rails控制台中测试内容.

gabrielf提出了一个很好的方法来保持变量本地化.这解决了从项目到项目的潜在问题.

env $(cat .env | xargs) rails
Run Code Online (Sandbox Code Playgroud)

我已经测试了这个 bash 3.2.51(1)-release


更新:

要忽略以开头的行#,请使用此(感谢Pete的评论):

export $(grep -v '^#' .env | xargs)
Run Code Online (Sandbox Code Playgroud)

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

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

更新:

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

export $(grep -v '^#' .env | xargs -d '\n')
Run Code Online (Sandbox Code Playgroud)

在GNU系统上或:

export $(grep -v '^#' .env | xargs -0)
Run Code Online (Sandbox Code Playgroud)

在BSD系统上.

  • 我提出了解决方案:env $(cat .env | xargs)rails (25认同)
  • 这是一个较短的变化`eval $(cat .env)rails` (16认同)
  • @BenjaminWheeler GNU linux有`-d`用于设置分隔符,所以我正在尝试`env $(cat .env | xargs -d'\n')rails`,但是如果`还是找不到文件,它仍然是错误的. env`有空格.知道为什么这不起作用吗? (6认同)
  • 谢谢,我喜欢这不需要在文件中添加任何内容 - 允许与Foreman(Procfile).env格式兼容. (4认同)
  • 如果任何env值都有空格,这似乎不起作用,虽然我实际上不确定用空格指定值的最佳/期望方法是什么.https://github.com/ddollar/foreman/issues/56说它应该像`export $(cat .env)`那样工作,但我不知道如何处理空格. (4认同)
  • 作为一个简短的解释(帮助我理解解决方案实际上在做什么),`cat .env`将读取`.env`文件,然后我们将结果传递给`xargs`,这是一个构建cli参数的辅助工具.最后,我们将参数和要运行的命令传递给`env`,它加载环境变量并执行命令. (4认同)
  • 不,“export $(grep -v '^#' .env | xargs -0)”在 GNU 系统上不正确。未引用的扩展仍然在空格上进行分词;xargs 发出 NUL 的事实并没有改变这一点。(某些版本的 bash 只是默默地从命令替换结果中删除 NUL,因此代码的确切行为取决于版本,因此无法可靠地测试)。就此而言,“xargs -d '\n'”在任何地方都是不必要的,因为换行符存在于 IFS 中,因此在命令替换结果中,它的解析方式与空格完全相同。 (3认同)
  • @BenWheeler这些空格似乎是子shell和xargs的问题.我无法使用空格和`env`或`export`,但使用`eval`解决方案可以工作(这是有道理的).如果你想导出每个变量而不是在本地脚本中使用它们,你可以在调用eval之前在子shell中做一些前置(使用`sed`?). (2认同)
  • 对于那些想知道为什么`| xargs` 是必需的:否则它会评估为类似 `export FOO='bar'\nSECONDVARIABLE='hi'` 的结果,它与 `export FOO='bar' SECONDVARIABLE='hi'` 的效果不同。 (2认同)
  • 如果值中有“空格”,这将不起作用。 (2认同)
  • 如果`.env`文件包含类似`sad = ----- 123`的内容,则不起作用 (2认同)
  • 将变量用引号引起来时,这种解决方案遇到了问题,例如REACT_APP_IFRAME_URI =“ http:// localhost:3000”。引号被解析,这不是故意的。 (2认同)

小智 332

-o allexport允许导出所有后续变量定义.+o allexport禁用此功能.

set -o allexport
source conf-file
set +o allexport
Run Code Online (Sandbox Code Playgroud)

  • 或单行“set -o allexport && 源conf-file && set +o allexport”。感谢@user4040650 的精彩分享。 (19认同)
  • @CMCDragonkai,对于POSIX,它将是`set -a; .的conf文件; 设+ A`. (13认同)
  • 奇迹般有效!即使`.env`文件中有注释.谢谢! (8认同)
  • 在一行`set -o allexport; 源配置文件; set + o allexport` (7认同)
  • 这在脚本中有效 (6认同)
  • 如果环境变量中包含空格,则此方法有效.许多其他人没有.虽然eval()方法确实如此,但我还是通过使用eval得到了一点点 (3认同)

anu*_*ava 166

你的方法的问题是exportwhile循环中一个子壳发生,这些变量不会在当前的shell(while循环的父shell)可用.

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

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"
Run Code Online (Sandbox Code Playgroud)

然后你需要使用以下命令在当前shell中的文件中获取:

. ./conf/prac1
Run Code Online (Sandbox Code Playgroud)

要么

source ./conf/prac1
Run Code Online (Sandbox Code Playgroud)

  • 虽然逐行读取文件并将每行传递给`export`并不理想,但也可以通过简单地在循环上使用输入重定向来解决问题:`读取行; 做......; 完成<./ conf/$ 1`. (4认同)
  • 你有一个更[干净的解决方案](https://unix.stackexchange.com/questions/79064/how-to-export-variables-from-a-file),我更喜欢`set -o allexport` (4认同)
  • 如果它不是来自文件,请使用`<<(生成输出的命令)` (3认同)
  • 如果使用 `,则不需要添加 `export` 前缀。./conf/prac1` (3认同)
  • 如果在系统之间使用这个 .env 文件,插入 `export` 会破坏它,比如 Java、SystemD 或其他工具 (2认同)
  • `awk '{print "export " $0}' envfile` 方便命令将导出添加到每行的开头 (2认同)
  • 虽然这确实有效,但我发现将“export”放在每个环境变量的开头会扰乱许多试图解析它们的程序(当所有内容都以“export”为前缀时,我无法使用 VS Code 调试器)例子)。 (2认同)

Dan*_*zyk 105

set -a
. ./env.txt
set +a
Run Code Online (Sandbox Code Playgroud)

如果env.txt是这样的话:

VAR1=1
VAR2=2
VAR3=3
...
Run Code Online (Sandbox Code Playgroud)

  • @Otto` -a`相当于`allexport`.换句话说,shell中的每个变量赋值都被"导出"到环境中(由多个子进程使用).另请参阅此文章https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html (10认同)
  • 嗨@亚历克西斯。这里使用的“.”命令本质上就像在终端上输入一样。您可以在终端上亲自尝试一下,看看结果如何。注释将被忽略,对其他变量的引用将起作用,只要它们之前已定义。 (2认同)

Hua*_*uan 60

我发现最有效的方法是:

export $(xargs < .env)
Run Code Online (Sandbox Code Playgroud)

解释

当我们有这样的.env文件时:

key=val
foo=bar
Run Code Online (Sandbox Code Playgroud)

运行xargs < .env会得到key=val foo=bar

所以我们会得到一个export key=val foo=bar,这正是我们所需要的!

局限性

  1. 它不处理值中有空格的情况。诸如 env 之类的命令会产生这种格式。– @Shardj

  • 对我来说完美的解决方案。 (2认同)

gsf*_*gsf 28

allexport此处的其他几个答案中提到了该选项,其中set -a就是快捷方式.获取.env实际上比循环和导出更好,因为它允许注释,空行,甚至是命令生成的环境变量.我的.bashrc包括以下内容:

# .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
}
Run Code Online (Sandbox Code Playgroud)

  • 这看起来不错,但是您离开目录时是否卸载环境变量? (2认同)

sel*_*van 25

eval $(cat .env | sed 's/^/export /')
Run Code Online (Sandbox Code Playgroud)

  • 我发现`cat .env | sed's/^ [^ $]/export /'`删除初始字符.即文件`A = foo \nB = bar \n`我得到`export = foo \nexport = bar \n`.这对跳过空白行更有效:`cat .env | sed'/ ^ $ /!s/^/export /'`. (2认同)

tut*_*uju 20

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

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

这会添加导出,在注释开头的行上保留注释.

.env内容

A=1
#B=2
Run Code Online (Sandbox Code Playgroud)

样品运行

$ sed -E -n 's/[^#]+/export &/ p' ~/.env
export A=1
#export B=2
Run Code Online (Sandbox Code Playgroud)

我发现这在构建用于在systemd单元文件中EnvironmentFile加载的文件时特别有用.


Nag*_*gev 17

我赞成了user4040650的答案,因为它既简单,又允许文件中的注释(即以#开头的行),这对我来说是非常理想的,因为可以添加解释变量的注释.只需在原始问题的上下文中重写.

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

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

# test that it works
echo "Ficheros: $MINIENTREGA_FICHEROS"
Run Code Online (Sandbox Code Playgroud)

以下是从设置文档中提取的:

这个内置是如此复杂,它应该有自己的部分.set允许您更改shell选项的值并设置位置参数,或显示shell变量的名称和值.

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

如果未提供任何选项或参数,则set将显示所有shell变量和函数的名称和值,并根据当前区域设置进行排序,格式可以重复用作设置或重置当前设置变量的输入.只读变量无法重置.在POSIX模式下,仅列出shell变量.

提供选项时,它们会设置或取消设置shell属性.选项(如果指定)具有以下含义:

-a创建或修改的每个变量或函数都具有export属性,并标记为导出到后续命令的环境.

这也是:

使用"+"而不是" - "会导致关闭这些选项.这些选项也可以在调用shell时使用.当前的选项集可以在$ - 中找到.


Jul*_*lle 15

不完全确定为什么,或者我错过了什么,但是在运行了大部分答案并失败之后。我意识到这个 .env 文件:

MY_VAR="hello there!"
MY_OTHER_VAR=123
Run Code Online (Sandbox Code Playgroud)

我可以简单地这样做:

MY_VAR="hello there!"
MY_OTHER_VAR=123
Run Code Online (Sandbox Code Playgroud)

输出: Hello there!

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

  • 您缺少的是这没有定义任何环境变量。它定义了参数。它们完全不同:您调用的任何命令都不会看到这些值,因为参数不是继承的。 (3认同)

Koi*_*ima 14

使用shdotenv

dotenv 支持 shell 和符合 POSIX 的 .env 语法规范
https://github.com/ko1nksm/shdotenv

eval "$(shdotenv)"
Run Code Online (Sandbox Code Playgroud)

用法

Usage: shdotenv [OPTION]... [--] [COMMAND [ARG]...]

  -d, --dialect DIALECT  Specify the .env dialect [default: posix]
                           (posix, ruby, node, python, php, go, rust, docker)
  -s, --shell SHELL      Output in the specified shell format [default: posix]
                           (posix, fish)
  -e, --env ENV_PATH     Location of the .env file [default: .env]
                           Multiple -e options are allowed
  -o, --overload         Overload predefined environment variables
  -n, --noexport         Do not export keys without export prefix
  -g, --grep PATTERN     Output only those that match the regexp pattern
  -k, --keyonly          Output only variable names
  -q, --quiet            Suppress all output
  -v, --version          Show the version and exit
  -h, --help             Show this message and exit
Run Code Online (Sandbox Code Playgroud)

要求

shdotenv 是一个嵌入 awk 脚本的单文件 shell 脚本。

  • POSIX shell(dash、bash、ksh、zsh 等)
  • awk(gawk、nawk、mawk、busybox awk)

  • @FHauri 也许有点矫枉过正,但这个问题有 43 个不同的答案:这真的是一个简单的问题吗?在我的用例中,我有一个用[Python方言](https://github.com/ko1nksm/shdotenv/blob/main/docs/dialects.md)编写的.env文件并将其应用于Bash。由于管理空间等的不同约定,不能简单地使用“source”。该工具及其差异分析对我来说绝对有用 (3认同)

gle*_*len 13

这是我的变体:

  with_env() {
    (set -a && . ./.env && "$@")
  }
Run Code Online (Sandbox Code Playgroud)

与之前的解决方案相比:

  • 它不会泄漏范围外的变量(来自.env的值不会暴露给调用者)
  • 不会破坏set选项
  • 返回已执行命令的退出代码
  • 使用posix兼容 set -a
  • 使用.而不是source避免bashism
  • 如果.env加载失败,则不会调用命令
with_env rails console
Run Code Online (Sandbox Code Playgroud)


jwf*_*arn 12

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

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

使用set -o allexport具有正确跳过注释而无需正则表达式的优点.

set +o它本身以bash以后可以执行的格式输出所有当前选项.同样方便:set -o单独输出所有当前选项的人性化格式.

  • 如果你需要取消设置只在`.env`中设置的变量,我可能会'exec env -i bash`在调用`eval`之前清除现有的环境. (2认同)

Jay*_*nki 12

改善Silas Paul的答案

在子shell上导出变量使它们成为命令的本地变量.

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


Til*_*gel 12

很抱歉添加另一个答案,但因为它很简单并且在很多情况下都有效,请尝试:

export $(< ~/my/.env)
Run Code Online (Sandbox Code Playgroud)


kol*_*pto 10

问题source在于它要求文件具有正确的 bash 语法,而一些特殊字符会破坏它:=, ", ', <, >, 和其他。所以在某些情况下你可以

source development.env
Run Code Online (Sandbox Code Playgroud)

它会起作用。

但是,此版本可承受值中的每个特殊字符

source development.env
Run Code Online (Sandbox Code Playgroud)

解释:

  • -a 意味着每个 bash 变量都将成为环境变量
  • /^#/d删除注释(以 开头的字符串#
  • /^\s*$/d 删除空字符串,包括空格
  • "s/'/'\\\''/g"用 替换每个单引号'\'',这是 bash 中生成引号的技巧序列:)
  • "s/=\(.*\)/='\1'/g"将每个a=b转换为a='b'

因此,您可以使用特殊字符:)

要调试此代码,请替换sourcecat,您将看到此命令生成的内容。

  • 为我工作,尽管我必须将最后一个 sed 替换为: ```sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/ g" -e "s/\ *=\ */=/g")``` 转义等号周围的任何空格 (2认同)

Jav*_*zzi 9

更简单:

  1. 抓住文件的内容
  2. 删除任何空行(只是让你分开一些东西)
  3. 删除任何评论(只是你添加了一些......)
  4. 添加export到所有行
  5. eval 整个东西

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

另一种选择(你不必跑eval(感谢@Jaydeep)):

export $(cat .env | sed -e /^$/d -e /^#/d | xargs)
Run Code Online (Sandbox Code Playgroud)

最后,如果您想让自己的生活变得非常轻松,请将其添加到您的~/.bash_profile:

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

(请source ~/.bash_profile确保您重新安装了您的BASH设置!!! 或者......只需创建一个新的选项卡/窗口并解决问题)您可以这样称呼它:source_envfile .env

  • 我必须从gitlab秘密变量的管道中读取.env文本:根据您的解决方案,该命令对我有用:`source &lt;(echo $(sed -E -n's / [^#] + /&/ p' &lt;(echo“ $ {2}” | tr -d'\ r')));`。gitlab以某种方式用Windows回车符保存秘密变量,所以我不得不用`tr -d'\ r'`来修剪它。 (2认同)

Fla*_*ken 8

我找到的最短方法:

您的.env档案:

VARIABLE_NAME="A_VALUE"
Run Code Online (Sandbox Code Playgroud)

然后就

. ./.env && echo ${VARIABLE_NAME}
Run Code Online (Sandbox Code Playgroud)

奖励:因为它是短线,所以在package.json文件中非常有用

  "scripts": {
    "echo:variable": ". ./.env && echo ${VARIABLE_NAME}"
  }
Run Code Online (Sandbox Code Playgroud)

  • 是的,“.”是“source”的同义词,因此具有相同的作用 (4认同)

Nic*_*aly 7

.env在 Mac 上使用 docker-compose 和files,并希望将其.env导入我的 bash shell(用于测试),这里的“最佳”答案是在以下变量上绊倒:

.env

NODE_ARGS=--expose-gc --max_old_space_size=2048
Run Code Online (Sandbox Code Playgroud)

解决方案

所以我最终使用eval, 并将我的 env var defs 用单引号括起来。

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

bash 版本

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


Ale*_*nyk 7

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

这个怎么运作

  1. 创建临时文件。
  2. 将所有当前环境变量值写入临时文件。
  3. 启用将源脚本中所有声明的变量导出到环境中。
  4. 读取.env文件。所有变量都将导出到当前环境中。
  5. 禁止将源脚本中所有声明的变量导出到环境中。
  6. 读取临时文件的内容。每行都会declare -x VAR="val"将每个变量导出到环境中。
  7. 删除临时文件。
  8. 取消设置保存临时文件名的变量。

特征

  • 保留已在环境中设置的变量的值
  • .env 可以评论
  • .env 可以有空行
  • .env不需要像其他答案(set -aset +a)中那样的特殊页眉或页脚
  • .env不需要export对每个值都有
  • 单线


gog*_*ogo 7

如果env支持该-S选项,则可以使用换行符或转义字符,如\n\t(请参阅env):

env -S "$(cat .env)" command
Run Code Online (Sandbox Code Playgroud)

.env 文件示例:

KEY="value with space\nnewline\ttab\tand
multiple
lines"
Run Code Online (Sandbox Code Playgroud)

测试:

KEY="value with space\nnewline\ttab\tand
multiple
lines"
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 6

我的要求是:

  • 没有前缀的简单 .env 文件export(为了与 dotenv 兼容)
  • 支持引号中的值:TEXT="alpha bravo charlie"
  • 支持以 # 和空行为前缀的注释
  • mac/BSD 和 linux/GNU 通用

根据上面的答案编译的完整工作版本:

  set -o allexport
  eval $(grep -v '^#' .env | sed 's/^/export /')
  set +o allexport
Run Code Online (Sandbox Code Playgroud)

  • 如果你在它们前面加上“export”,那么“-o allexport”有什么意义呢? (3认同)

Ext*_*tor 5

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

. ./minientrega.sh
Run Code Online (Sandbox Code Playgroud)

方法也可能存在问题cat | while read.我建议使用这种方法while read line; do .... done < $FILE.

这是一个工作示例:

> 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
Run Code Online (Sandbox Code Playgroud)


Gus*_*uss 5

我对之前建议的解决方案有疑问:

  • @anubhava 的解决方案使得编写 bash 友好的配置文件变得非常烦人且非常快,而且 - 你可能不想总是导出你的配置。
  • 当您的变量包含空格或其他字符时,@Silas Paul 解决方案会中断,这些字符在引用值中运行良好,但$()会造成混乱。

这是我的解决方案,在我看来,它仍然非常糟糕 - 并且不能解决 Silas 解决的“仅导出到一个孩子”的问题(尽管您可以在子 shell 中运行它以限制范围):

source .conf-file
export $(cut -d= -f1 < .conf-file)
Run Code Online (Sandbox Code Playgroud)


Vic*_*man 5

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

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a
Run Code Online (Sandbox Code Playgroud)


Tud*_*soi 5

我的.env:

#!/bin/bash
set -a # export all variables

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

set +a #stop exporting variables
Run Code Online (Sandbox Code Playgroud)

调用:

source .env; echo $USER; echo $PASS
Run Code Online (Sandbox Code Playgroud)

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


lel*_*uch 5

这是我对此的看法。我有以下要求:

  • 忽略注释行
  • 值中允许有空格
  • 允许空行
  • 能够传递自定义环境文件,同时默认为 .env
  • 允许导出以及内联运行命令
  • 如果 env 文件不存在则退出
source_env() {
  env=${1:-.env}
  [ ! -f "${env}" ] && { echo "Env file ${env} doesn't exist"; return 1; }
  eval $(sed -e '/^\s*$/d' -e '/^\s*#/d' -e 's/=/="/' -e 's/$/"/' -e 's/^/export /' "${env}")
}
Run Code Online (Sandbox Code Playgroud)

将函数保存到 .bash_profile 或等效文件后的用法:

source_env                # load default .env file
source_env .env.dev       # load custom .env file
(source_env && COMMAND)   # run command without saving vars to environment
Run Code Online (Sandbox Code Playgroud)

受到哈维尔和其他一些评论的启发。


小智 5

我的版本:

我打印文件,删除注释行、空行,然后将键/值从“=”符号中拆分出来。然后我只需应用导出命令。

此解决方案的优点是文件可以在值中包含特殊字符,例如管道、html 标记等,并且该值不必像真正的属性文件一样用引号引起来。

# Single line version
cat myenvfile.properties | grep -v '^#' | grep '=' | while read line; do IFS=\= read k v <<< $line; export $k="$v"; done

# Mutliline version:
cat myenvfile.properties | grep -v '^#' | grep '=' | while read line; do 
  IFS=\= read k v <<< $line
  export $k="$v"
done

Run Code Online (Sandbox Code Playgroud)