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
但同样的问题仍然适用
也许还有其他方法可以做到这一点,我只需要使用我传递的文件的环境变量作为脚本的参数。
这可能会有所帮助:
export $(cat .env | xargs) && rails c
我使用它的原因是如果我想在我的 rails 控制台中测试 .env
东西。
gabrielf 提出了一种将变量保持在本地的好方法。这解决了从一个项目到另一个项目时的潜在问题。
env $(cat .env | xargs) rails
我用 bash 3.2.51(1)-release
对此进行了测试
更新:
要忽略以 #
开头的行,请使用此命令(感谢 Pete's comment):
export $(grep -v '^#' .env | xargs)
如果您想要 unset
文件中定义的所有变量,请使用:
unset $(grep -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)
更新:
要同时处理带有空格的值,请使用:
export $(grep -v '^#' .env | xargs -d '\n')
在 GNU 系统上——或者:
export $(grep -v '^#' .env | xargs -0)
在 BSD 系统上。
在 this answer 中,您可以通过以下方式自动检测操作系统:
export-env.sh
#!/bin/sh
## Usage:
## . ./export-env.sh ; $COMMAND
## . ./export-env.sh ; echo ${MINIENTREGA_FECHALIMITE}
unamestr=$(uname)
if [ "$unamestr" = 'Linux' ]; then
export $(grep -v '^#' .env | xargs -d '\n')
elif [ "$unamestr" = 'FreeBSD' ]; then
export $(grep -v '^#' .env | xargs -0)
fi
-o allexport
允许导出所有以下变量定义。 +o allexport
禁用此功能。
set -o allexport
source conf-file
set +o allexport
.env
文件中有评论。谢谢!
set -o allexport; source conf-file; set +o allexport
中
set -a; . conf-file; set +a
。
您的方法的问题是 while
循环中的 export
发生在子 shell 中,并且这些变量在当前 shell(while 循环的父 shell)中不可用。
在文件本身中添加 export
命令:
export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"
然后,您需要使用以下命令在当前 shell 中获取文件:
. ./conf/prac1
或者
source ./conf/prac1
export
并不理想,但也可以通过简单地在循环上使用输入重定向来解决该问题:while read line; do ... ; done < ./conf/$1
。
< <(commands that generate output)
set -o allexport
export
会破坏它,例如 Java、SystemD 或其他工具
awk '{print "export " $0}' envfile
将导出添加到每行开头的便捷命令
set -a
. ./env.txt
set +a
如果 env.txt
是这样的:
VAR1=1
VAR2=2
VAR3=3
...
解释 -a 等价于 allexport。换句话说,shell 中的每个变量分配都被导出到环境中(供多个子进程使用)。更多信息可在 Set builtin documentation 中找到:
-a 创建或修改的每个变量或函数都被赋予导出属性并标记为导出到后续命令的环境。使用“+”而不是“-”会导致这些选项被关闭。这些选项也可以在调用 shell 时使用。当前的选项集可以在 $- 中找到。
VAR2=$VAR1
?
.
命令本质上类似于在终端上键入。您可以在终端上自己尝试一下,看看结果如何。注释将被忽略,并且对其他变量的引用将起作用,只要它们之前已定义。
我发现最有效的方法是:
export $(xargs < .env)
解释
当我们有这样的 .env
文件时:
key=val
foo=bar
运行 xargs < .env
将得到 key=val foo=bar
所以我们会得到一个export key=val foo=bar
,这正是我们所需要的!
局限性
它不处理值中有空格的情况。诸如 env 之类的命令会产生这种格式。 – @Shardj
此处的其他几个答案中提到了 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
}
VAR=
将它们设置为空白。
eval $(cat .env | sed 's/^/export /')
eval $(cat .env | sed 's/^[^$]/export /')
可以让您有空行以提高可读性。
cat .env | sed 's/^[^$]/export /'
去掉了初始字符。即对于文件A=foo\nB=bar\n
,我得到export =foo\nexport =bar\n
。这对于跳过空白行效果更好:cat .env | sed '/^$/! s/^/export /'
。
cat
:eval $(sed 's/^/export /' .env)
也可以。)
sed 's/^/export /' .env
source
的问题在于它要求文件具有正确的 bash 语法,并且一些特殊字符会破坏它:=
、"
、'
、<
、>
等。所以在某些情况下你可以
source development.env
它会起作用。
但是,此版本可以承受值中的每个特殊字符:
set -a
source <(cat development.env | \
sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
set +a
解释:
-a 表示每个 bash 变量都将成为环境变量
/^#/d 删除注释(以 # 开头的字符串)
/^\s*$/d 删除空字符串,包括空格
"s/'/'\\\''/g" 用 '\'' 替换每个单引号,这是 bash 中产生引号的技巧序列 :)
"s/=\(.*\)/='\1'/g" 将每个 a=b 转换为 a='b'
结果,您可以使用特殊字符:)
要调试此代码,请将 source
替换为 cat
,您将看到此命令产生的结果。
这是另一个 sed
解决方案,它不运行 eval 或需要 ruby:
source <(sed -E -n 's/[^#]+/export &/ p' ~/.env)
这增加了导出,将注释保留在以注释开头的行上。
.env 内容
A=1
#B=2
样品运行
$ sed -E -n 's/[^#]+/export &/ p' ~/.env
export A=1
#export B=2
我发现这在构造这样的文件以在 systemd unit file, with EnvironmentFile
中加载时特别有用。
我赞成 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"
以下是从 set documentation 中提取的:
这个内置函数非常复杂,值得拥有自己的部分。 set 允许您更改 shell 选项的值并设置位置参数,或显示 shell 变量的名称和值。 set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument ...] set [+abefhkmnptuvxBCEHPT] [+o option-name] [argument ...] 如果没有提供选项或参数,set 显示所有 shell 的名称和值变量和函数,根据当前语言环境排序,格式可重复用作设置或重置当前设置变量的输入。只读变量不能被重置。在 POSIX 模式下,仅列出 shell 变量。提供选项时,它们会设置或取消设置 shell 属性。选项(如果指定)具有以下含义: -a 创建或修改的每个变量或函数都被赋予导出属性并标记为导出到后续命令的环境。
这也是:
使用“+”而不是“-”会导致这些选项被关闭。这些选项也可以在调用 shell 时使用。当前的选项集可以在 $- 中找到。
不完全确定为什么,或者我错过了什么,但是在运行了大多数答案并失败之后。我意识到这个 .env 文件:
MY_VAR="hello there!"
MY_OTHER_VAR=123
我可以简单地这样做:
source .env
echo $MY_VAR
输出:Hello there!
似乎在 Ubuntu linux 中工作得很好。
MY_VAR
将包含引号作为值的一部分 :) docs.docker.com/compose/env-file
改进 Silas Paul 的回答
将变量导出到子shell 会使它们成为命令的本地变量。
(export $(cat .env | xargs) && rails c)
(set -a; source dev.env; set +a; rails c)
来获得采购的好处(例如脚本可以执行)。
SAVE=$(set +o | grep allexport) && set -o allexport && . .env; eval "$SAVE"
这将保存/恢复您的原始选项,无论它们是什么。
使用 set -o allexport
的优点是可以在没有正则表达式的情况下正确跳过评论。
set +o
本身会以 bash 以后可以执行的格式输出您当前的所有选项。也很方便:set -o
本身,以人性化的格式输出您当前的所有选项。
.env
中设置的变量,我可能会 exec env -i bash
在调用 eval
之前清除现有环境。
我发现的最短方法:
您的 .env
文件:
VARIABLE_NAME="A_VALUE"
然后就
. ./.env && echo ${VARIABLE_NAME}
奖励:因为它是一个简短的单行,它在 package.json
文件中非常有用
"scripts": {
"echo:variable": ". ./.env && echo ${VARIABLE_NAME}"
}
VARIABLE_NAME="A_VALUE"
.
是 source
的同义词,因此也是如此
这是我的变体:
with_env() {
(set -a && . ./.env && "$@")
}
与之前的解决方案相比:
它不会泄漏范围外的变量(来自 .env 的值不会暴露给调用者)
不破坏设置选项
返回执行命令的退出代码
使用 posix 兼容的 set -a
使用 .而不是来源以避免bashism
如果 .env 加载失败,则不会调用命令
with_env rails console
set -a && . ./.env && "$@" && echo "your comand here"
如果 env
支持 -S
选项,则可以使用换行符或转义字符,例如 \n
或 \t
(请参阅 env):
env -S "$(cat .env)" command
.env
文件示例:
KEY="value with space\nnewline\ttab\tand
multiple
lines"
测试:
env -S "$(cat .env)" sh -c 'echo "$KEY"'
更简单:
抓取文件的内容 删除所有空行(以防你分隔了一些东西)删除所有注释(以防你添加了一些......)添加导出到所有行评估整个事情
eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')
另一种选择(您不必运行 eval
(感谢@Jaydeep)):
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
source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "$tr -d '\r'
" | tr -d '\r')) );
。不知何故,gitlab 用 windows 回车保存了秘密变量,所以我不得不用 tr -d '\r'
修剪它。
我在 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 \'{}\')
重击版本
$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
使用 shdotenv
dotenv 支持 shell 和 POSIX 兼容的 .env 语法规范
https://github.com/ko1nksm/shdotenv
eval "$(shdotenv)"
用法
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
要求
shdotenv 是一个带有嵌入式 awk 脚本的单文件 shell 脚本。
POSIX shell(dash、bash、ksh、zsh 等)
awk(gawk、nawk、mawk、busybox awk)
source
。该工具及其对差异的分析对我来说绝对有用
您可以使用原始脚本来设置变量,但您需要通过以下方式调用它(使用独立点):
. ./minientrega.sh
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
test.conf
作为脚本文件进行评估。这使它变得更好。除非您确实需要,否则不允许编写脚本会更安全,特别是如果有人没有意识到正在发生的事情(或忘记了)。
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
这个怎么运作
创建临时文件。将所有当前环境变量值写入临时文件。启用将源脚本中所有声明的变量导出到环境。读取 .env 文件。所有变量都将导出到当前环境中。禁止将源脚本中所有声明的变量导出到环境。读取临时文件的内容。每一行都会声明 -x VAR="val" 将每个变量导出到环境中。删除临时文件。取消设置保存临时文件名的变量。
特征
保留环境中已设置的变量值
.env 可以有评论
.env 可以有空行
.env 不需要像其他答案中那样的特殊页眉或页脚(set -a 和 set +a)
.env 不需要为每个值都导出
单线
在其他答案的基础上,这是一种仅导出文件中行子集的方法,包括带有空格的值,例如 PREFIX_ONE="a word"
:
set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a
我的.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
调用:
source .env; echo $USER; echo $PASS
参考https://unix.stackexchange.com/questions/79068/how-to-export-variables-that-are-set-all-at-once
我的要求是:
没有导出前缀的简单 .env 文件(为了与 dotenv 兼容)
引号中的支持值:TEXT="alpha bravo charlie"
支持以 # 和空行为前缀的注释
mac/BSD 和 linux/GNU 通用
从上面的答案编译的完整工作版本:
set -o allexport
eval $(grep -v '^#' .env | sed 's/^/export /')
set +o allexport
我的版本:
我打印文件,删除注释行,空行,并从“=”符号中拆分键/值。然后我只应用导出命令。
此解决方案的优点是文件可以在值中包含特殊字符,如管道、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
我对早期建议的解决方案有疑问:
@anubhava 的解决方案使编写 bash 友好的配置文件非常烦人,而且 - 您可能不想总是导出您的配置。
@Silas Paul 解决方案在您的变量中有空格或其他字符在引用值中运行良好时会中断,但是 $() 会弄得一团糟。
这是我的解决方案,它仍然是非常糟糕的 IMO - 并没有解决 Silas 解决的“仅导出给一个孩子”问题(尽管您可能可以在子 shell 中运行它以限制范围):
source .conf-file
export $(cut -d= -f1 < .conf-file)
修改自@Dan Kowalczyk
我把它放在 ~/.bashrc
中。
set -a
. ./.env >/dev/null 2>&1
set +a
与 Oh-my-Zsh 的 dotenv 插件很好地交叉兼容。 (有 Oh-my-bash,但没有 dotenv 插件。)
我用这个:
source <(cat .env \
| sed -E '/^\s*#.*/d' \
| tr '\n' '\000' \
| sed -z -E 's/^([^=]+)=(.*)/\1\x0\2/g' \
| xargs -0 -n2 bash -c 'printf "export %s=%q;\n" "${@}"' /dev/null)
首先删除评论:
sed -E '/^\s*#.*/d'
然后转换为空分隔符而不是换行符:
tr '\n' '\000'
然后用null替换equal:
sed -z -E 's/^([^=]+)=(.*)/\1\x0\2/g'
然后将对打印到有效的引用 bash 导出(使用 bash printf for %q):
xargs -0 -n2 bash -c 'printf "export %s=%q;\n" "${@}"' /dev/null
然后最终采购所有这些。
它应该适用于几乎所有具有所有特殊字符的情况。
首先,创建一个环境文件,该文件将包含如下环境的所有键值对,并在我的例子中将其命名为 env_var.env
MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"
然后创建一个脚本,该脚本将导出 Python 环境的所有环境变量,如下所示,并将其命名为 export_env.sh
#!/usr/bin/env bash
ENV_FILE="$1"
CMD=${@:2}
set -o allexport
source $ENV_FILE
set +o allexport
$CMD
该脚本将第一个参数作为环境文件,然后导出该文件中的所有环境变量,然后运行该命令。
用法:
./export_env.sh env_var.env python app.py
我如何保存变量:
printenv | sed 's/\([a-zA-Z0-9_]*\)=\(.*\)/export \1="\2"/' > myvariables.sh
我如何加载它们
source myvariables.sh
不定期副业成功案例分享
-d
用于设置分隔符,所以我正在尝试env $(cat .env | xargs -d '\n') rails
,但如果.env
有空格,它仍然会出现找不到文件的错误。知道为什么这不起作用吗?eval $(cat .env) rails
cat .env
将读取.env
文件,然后我们将结果通过管道传递给xargs
,它是构造 cli 参数的辅助工具。最后,我们将参数和要运行的命令都传递给env
,它会加载环境变量并执行命令。REACT_APP_IFRAME_URI="http://localhost:3000"
。引号被解析,这不是预期的。