为什么不能在bash 4.1.2中访问带破折号的环境变量?

Mat*_*bst 5 bash shell centos

在CentOS 5主机上(使用bash 3.2.32),我们使用Ruby(1.8.7)来实现

ENV['AWS_foo-bar_ACCESS_KEY'] = xxxxx
Run Code Online (Sandbox Code Playgroud)

然后,使用bash,我们运行一个shell脚本:

BUCKET_NAME=$1
AWS_ACCESS_KEY_ID_VAR="AWS_${BUCKET_NAME}_ACCESS_KEY_ID"
AWS_ACCESS_KEY_ID="${!AWS_ACCESS_KEY_ID_VAR}"
export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
Run Code Online (Sandbox Code Playgroud)

这在CentOS 5上运行良好.

但是,在CentOS 6上(使用bash 4.1.2),我们得到了错误

-bash: export: `AWS_foo-bar_ACCESS_KEY_ID=xxxxx': not a valid identifier
Run Code Online (Sandbox Code Playgroud)

我们的理解是,这失败了,因为-变量名中不允许这样做.但是为什么这个工作在bash 3.2而不是bash 4.1?

Cha*_*ffy 15

"为什么"几乎是不相关的:POSIX标准非常清楚,export只需要支持有效名称的参数,任何带破折号的东西都不是有效名称.因此,不需要POSIX shell来支持使用破折号,间接扩展或其他方式导出或扩展变量名称.

值得注意的是,ShellShock--由环境内容的草率处理引起的一个主要安全漏洞 - 在当前CentOS 6更新存储库中出现的bash 4.1中得到修复; 在一个产生安全漏洞的区域增加严谨性应该不足为奇.

本答案的其余部分将集中于演示POSIX明确允许甚至需要bash 4.1的新行为 - 因此先前的行为是未定义的实现工件.


引用POSIX的环境变量:

这些字符串的格式为name = value; 名称不得包含字符'='.对于可在符合IEEE Std 1003.1-2001的系统中移植的值,该值应由便携式字符集中的字符组成(NUL除外,如下所示).没有与环境中字符串顺序相关的含义.如果进程环境中的多个字符串具有相同的名称,则后果未定义.

IEEE Std 1003.1-2001的Shell和Utilities卷中的实用程序使用的环境变量名称仅由大写字母,数字和便携式字符集中定义的字符的"_"(下划线)组成,并且不以数字开头.实现可以允许其他字符; 申请应容忍此类名称的存在.大写和小写字母应保留其独特的身份,不得折叠在一起.包含小写字母的环境变量名称的名称空间保留给应用程序.应用程序可以使用此名称空间中的名称定义任何环境变量,而无需修改标准实用程序的行为.

注意:其他应用程序可能难以处理以数字开头的环境变量名称.因此,不建议在任何地方使用此类名称.

从而:

  • 工具(包括外壳)都需要用大写字母,小写字母,数字(除了在第一位置)和下划线完全支持环境变量名.
  • 工具(包括shell)应该容忍其他名称 - 这意味着它们不应该在其存在时崩溃或行为不当 - 但不需要支持它们.

最后,明确允许shell丢弃环境变量名,这些名称也不是shell变量名.从相关标准来看:

未指定环境变量是否在调用时传递给shell,但未用于初始化shell变量(请参阅Shell变量),因为它们具有无效名称,包含在传递给execl()的环境中(如果是execl) ()如上所述失败)到新shell.


而且,定义有效shell名称的定义很明确:

名称 - 在shell命令语言中,单词由可移植字符集中的下划线,数字和字母组成.名称的第一个字符不是数字.

值得注意的是,只有下划线(不是短划线)被认为是符合POSIX标准的shell中有效名称的一部分.


...和POSIX规范export明确使用单词"name"(它在上面引用的文本中定义),并将其描述为适用于"变量"(shell变量,对其名称的限制也受限制)引用本文档的其他部分):

shell应将export属性赋予与指定名称对应的变量,这将使它们处于随后执行的命令的环境中.如果变量的名称后跟= word,则该变量的值应设置为word.


以上所有内容 - 如果您的操作系统提供了一个/proc/self/environ代表您在进程启动时的环境变量的状态(在shell之前,允许这样做,可能会丢弃任何在shell中没有有效名称的变量) ,您可以使用无效名称提取内容,如下所示:

# using a lower-case name where possible is in line with POSIX guidelines, see above
aws_access_key_id_var="AWS_${BUCKET_NAME}_ACCESS_KEY_ID"
while IFS= read -r -d '' var; do
  [[ $var = "$aws_access_key_id_var"=* ]] || continue
  val=${var#"${aws_access_key_id_var}="}
  break
done </proc/self/environ
echo "Extracted value: $val"
Run Code Online (Sandbox Code Playgroud)