我有一个脚本,可以解析基本.env
文件并将其内容导出为环境变量,然后再采取进一步操作:
set -eu
test -f .env && load_dotenv
exec ./foobar x y z "$@"
但是,这会导致定义的环境变量.env
优先于用户 shell 中定义的环境变量。我想要相反的行为:我希望在 shell 中显式设置的环境变量优先于文件中定义的任何内容.env
。
因此,我希望在运行之前“保存”当前环境load_dotenv
,然后在运行后“重新应用”保存的环境load_dotenv
:
set -eu
user_env="$( env )"
test -f .env && load_dotenv
reapply_env "$user_env" # ???
exec ./foobar x y z "$@"
我如何实现reapply_env
,以便它可以处理任意环境变量?据我所知,除此之外的任何字节\0
在环境变量中都是有效的,所以我想确保这适用于任何任意环境变量。例如,在环境变量中嵌入换行符有合法的(尽管不常见)用例。
我愿意接受特定于 shell 的答案,但假设我使用的是 Bourne 风格的 shell(Bash、Ksh、Zsh 等)。
忽略安全问题。假设我在受信任、受控的环境中操作,并且任何像“欺骗开发人员下载恶意.env
文件”这样的场景都涉及更广泛的安全问题,超出了本问题的范围。
编辑:如何避免覆盖环境变量是一个很好的问题,但这不是我有兴趣回答这个特定问题的内容。如果还没有重复的内容,我可以为此单独发布一篇。
答案1
Bash 能够在设置环境变量之前对其进行测试。所以你可以.env
这样写你的文件:
VAR1="default value"
VAR2=${VAR2:="default value"}
现在,如果您有一个新会话(批处理或终端)并执行以下操作:
$ source .env
$ echo $VAR1 - $VAR2
default value - default value
$ VAR1="my value"
$ VAR2="my value"
$ source .env
$ echo $VAR1 - $VAR2
default value - my value
该语法描述如下:https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
ksh
和都zsh
具有相似的能力,对于“如果变量不存在则为该变量赋值”任务具有相同的语法:
https://www.ibm.com/docs/en/aix/7.1?topic=shell-parameter-substitution-in-korn-posix
https://zsh.sourceforge.io/Doc/Release/Expansion.html#Parameter-Expansion
答案2
在 Bash 中(不确定支持的最低版本),export
内置函数会发出一个可以eval
-ed 或source
-d 的有效脚本:
#!/usr/bin/env bash
export FOO=$'a\nb'
export > ./user-env.bash
env -u FOO bash -c '
source ./user-env.bash ;
sh -c "printf \"%q\n\" \"$FOO\""
'
输出应为:
$'a\nb'
Zsh 也发出一个有效的脚本,但它确实不是包含任何export
声明,而 Bash 版本包含declare -x
在每行的开头。所以我仍在寻找 Zsh 解决方案。
答案3
如果你使用的是Linux,你可以阅读以下内容/proc/self/environ
来保存环境变量:
myenv=()
while IFS= read -d '' -r var; do
myenv+=("$var");
done < /proc/self/environ
然后,加载 env 文件后,使用该数组再次设置这些值(假设变量名称不包含=
):
for i in "${myenv[@]}";
do
export "$i";
done