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

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


当前回答

sh -ac '. conf-file; yourcommand'

-a开关导出所有变量,以便程序可以使用它们。

与较长的版本set -a;. 配置文件;+一个;yourcommand 使用sh可以确保导出的值不会永久地污染当前环境。它仅为在子shell中运行的程序提供和导出变量。

其他回答

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

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

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

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

如果你得到一个错误,因为你的一个变量包含了一个包含空格的值,你可以尝试重置bash的IFS(内部字段分隔符)为\n,让bash解释cat .env结果为env可执行文件的参数列表。

例子:

IFS=$'\n'; env $(cat .env) rails c

参见:

http://tldp.org/LDP/abs/html/internalvariables.html#IFSREF https://unix.stackexchange.com/a/196761

在这里的几个其他答案中提到了alleexport选项,其中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
}

SAVE=$(set +o | grep allexport) && set -o allexport && ..环境;评估“$SAVE”

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

使用set -o alleexport的优点是可以在没有正则表达式的情况下正确地跳过注释。

Set +o本身以bash稍后可以执行的格式输出所有当前选项。同样方便的是:set -o本身以人类友好的格式输出所有当前选项。

出口是答案。

交互式exercices

由于shell是交互式的,您可以尝试内联!

$ mkdir conf && printf 'MINIENTREGA_%s="%s"\n' FECHALIMITE 2011-03-31 FICHEROS \
    "informe.txt programa.c" DESTINO ./destino/entrega-prac1 >conf/prac1 

$ set -- prac1
$ while read -r line; do export $line; done <"conf/$1"
bash: export: `programa.c"': not a valid identifier
$ while read -r line; do LANG=C export "$line"; done <"conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
"informe.txt programa.c"

注意双引号!

源代码。

$ set -- prac1
$ . "conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
informe.txt programa.c

好的,现在是关于出口的问题

导出命令告诉shell将shell变量导出到环境…因此,在使用任何子进程(如ruby, python, perl甚至其他shell脚本)之前,您必须导出脚本变量。

清除先前的操作以进行进一步演示

$ declare +x MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ unset MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO

所以在交互式shell中,最简单的方法是运行另一个shell:

$ set -- prac1
$ . "conf/$1"
$ bash -c 'declare -p MINIENTREGA_FICHEROS'
bash: line 1: declare: MINIENTREGA_FICHEROS: not found

$ export MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ bash -c 'declare -p MINIENTREGA_FICHEROS'
declare -x MINIENTREGA_FICHEROS="informe.txt programa.c"

用于导出变量的示例外壳包装器

最小的包装,没有安全问题(当其他用户编辑脚本时要注意!!)。

#!/bin/sh

while IFS== read -r varname _;do
    case $varname in
         *[!A-Za-z0-9_]* | '' ) ;;
         * ) export $varname ;;
    esac
done <conf/$1
. conf/$1

busybox sh -c 'set | grep MINIENTREGA'

以prac1作为参数运行,应该产生:

MINIENTREGA_DESTINO='./destino/entrega-prac1'
MINIENTREGA_FECHALIMITE='2011-03-31'
MINIENTREGA_FICHEROS='informe.txt programa.c'

在细

获取配置文件与声明变量是一样的。 导出你的变量是一个指令,让shell在全局环境中为任何子进程共享他的变量。

这两个操作可以任意顺序进行。唯一的要求是在尝试运行任何子流程之前完成这两个操作。

你甚至可以同时进行这两种操作,通过在配置文件中导出,例如:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"