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

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


当前回答

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

忽略注释行 值中允许有空格 允许空行 能够在默认为.env时传递自定义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}")
}

将函数保存到.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

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

其他回答

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

. ./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

首先,创建一个环境文件,其中包含如下所示环境的所有键-值对,并将其命名为任何您喜欢的名称,在我的示例中为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

使用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, etc) awk (gawk, nawk, mawk, busybox awk)

posix兼容的解决方案(不依赖于bash)

正如其他人所注意到的,在这里使用for/while循环的问题是,变量在shell及其子shell之间不共享。然而,我们能做的是使用args/stdin/stdout在shell之间传递文本。

在subshell中设置环境变量在我们获取脚本源代码时是没有帮助的

变量不会向上传播,但我们知道可以将文本发送回去。这个文本也可以是代码,我们可以用eval在当前shell中求值。

如果我们生成用于设置所有环境变量的代码,然后对结果进行计算呢?

create_exports_script() {
    echo "$1" | while read line; do
        echo "export $line"
    done
}

file_contents=$(cat "./conf/myconf.env")
eval $(create_exports_script "$file_contents")

bash中的这种函数式元编程非常灵活。您还可以用这种方式生成bash/sh以外的其他语言。

我的看法是:

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

这种解决方案的优点是文件可以在值中包含特殊字符,如管道、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