为什么这个 Bash 脚本没有继承所有的环境变量?

Nic*_*mas 5 bash

我正在尝试一些非常简单的事情:

PEOPLE=(
  "nick"
  "bob"
)
export PEOPLE="$(IFS=, ; echo "${PEOPLE[*]}")"
echo "$PEOPLE"  # prints 'nick,bob'
./process-people.sh
Run Code Online (Sandbox Code Playgroud)

出于某种原因,process-people.sh没有看到$PEOPLE. 就像,如果 I echo "$PEOPLE"from inside process-people.sh,它会打印一个空行。

据我了解,通过调用创建的子进程./process-people.sh应该继承父进程的所有环境变量,包括$PEOPLE. 然而,我在 Bash 3.2.57(1)-release 和 4.2.46(2)-release 上都试过这个,但它不起作用。

这里发生了什么?

Nic*_*mas 9

是将 Bash 数组的元素连接到 string的巧妙解决方案。您知道在 Bash中不能将数组变量导出到环境吗?如果变量不在环境中,那么子进程将看不到它。

啊。但是您不是在导出数组,是吗。您正在将数组转换为字符串,然后将其导出。所以它应该工作。

但这是 Bash!历史的各种事故合谋给你一个手指。

正如@PesaThe 和@chepner 在下面的评论中指出的那样,您实际上无法将 Bash 数组变量转换为字符串变量。根据数组上Bash 参考

引用一个没有下标的数组变量相当于引用一个下标为 0 的变量。

因此,当您调用先前分配给数组值的export PEOPLE=...wherePEOPLE时,您实际上在做的是PEOPLE[0]=.... 这是一个更完整的例子:

PEOPLE=(
  "nick"
  "bob"
)
export PEOPLE="super"
echo "$PEOPLE"  # masks the fact that PEOPLE is still an array and just prints 'super'
echo "${PEOPLE[*]}"  # prints 'super bob'
Run Code Online (Sandbox Code Playgroud)

不幸的是,export默默地未能将数组导出到环境(它返回0),并且在某些情况下Bash 等同ARRAY_VARIABLE于令人困惑ARRAY_VARIABLE[0]。我们只需要将其归结为历史和向后兼容性的组合。

这是您的问题的有效解决方案:

PEOPLE_ARRAY=(
  "nick"
  "bob"
)
export PEOPLE="$(IFS=, ; echo "${PEOPLE_ARRAY[*]}")"
echo "$PEOPLE"  # prints 'nick,bob'
./process-people.sh
Run Code Online (Sandbox Code Playgroud)

这里的关键是将数组和派生字符串分配给不同的变量。由于PEOPLE是一个合适的字符串变量,它可以很好地导出process-people.sh并按预期工作。

无法将 Bash 数组变量直接更改为字符串变量。一旦一个变量被赋予一个数组值,它就成为一个数组变量。将它改回字符串变量的唯一方法是销毁它unset并重新创建它。

Bash 有几个方便的命令用于检查对调查此类问题有用的变量:

printenv PEOPLE  # prints 'nick,bob'
declare -p PEOPLE_ARRAY  # prints: declare -ax PEOPLE_ARRAY='([0]="nick" [1]="bob")'
Run Code Online (Sandbox Code Playgroud)

printenv只会返回环境变量的值 vs. echo,无论变量是否已正确导出,它都会打印结果。

declare -p将显示变量的完整值,没有与包含或遗漏数组索引引用(例如ARRAY_VARIABLE[*])相关的问题。

  • `引用一个没有下标的数组变量相当于引用一个下标为 0 的变量。` 所以`PEOPLE=$(...)` 所做的只是将命令替换的结果分配给 `PEOPLE[0]`。所以没有转换,它仍然是一个无法导出的数组:) (3认同)