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,但同样的问题仍然适用

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


当前回答

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

set -o allexport
source conf-file
set +o allexport

其他回答

这是我的变种:

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

与之前方案相比:

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

with_env rails console

当我试图在shell中重用Docker -env-files时,我遇到了这个线程。它们的格式与bash不兼容,但很简单:name=value,没有引号,没有替换。它们还会忽略空行和#注释。

我不能完全让它与posix兼容,但这里有一个应该在bash-like shell中工作(在OSX 10.12.5的zsh和Ubuntu 14.04的bash中测试):

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':不是有效的标识符 Bash: export: `org.spring。Config =something':不是有效的标识符 并且它不会处理传递语法(一个纯FOO)

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

. ./minientrega.sh

此外,在读取方法时,cat |可能会出现问题。我建议在读行时使用这种方法;做……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

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

@anubhava的解决方案使得编写bash友好的配置文件非常烦人,而且-你可能不想总是导出你的配置。 @Silas Paul解决方案中断时,你的变量有空格或其他字符,在引号值中工作得很好,但$()使混乱。

以下是我的解决方案,在我看来仍然很糟糕——并且不能解决Silas提出的“只导出到一个孩子”的问题(尽管你可能可以在子shell中运行它来限制范围):

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

值中的空格

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

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

我发现了2个解决方案,这些值支持空行和注释。

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

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

和一个读行在循环基于@john1024答案

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

这里的关键是使用声明-x并将line放在双引号中。我不知道为什么,但当你将循环代码重新格式化为多行时,它不会工作-我不是bash程序员,我只是狼吞虎咽地把这些放在一起,它对我来说仍然是魔法:)