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

时间:2016-05-02 18:35:02

标签: bash shell centos

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

ENV['AWS_foo-bar_ACCESS_KEY'] = xxxxx

然后,使用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}

这在CentOS 5上运行良好。

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

-bash: export: `AWS_foo-bar_ACCESS_KEY_ID=xxxxx': not a valid identifier

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

1 个答案:

答案 0 :(得分:13)

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

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

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

quote POSIX on environment variables

  

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

     

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

     

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

因此:

  • 工具(包括shell) required 以完全支持具有大写和小写字母,数字(第一个位置除外)和下划线的环境变量名称。
  • 工具(包括shell)应该容忍其他名称 - 这意味着他们不应该在他们面前崩溃或行为不端 - 但不要求他们支持他们。

最后,明确允许shell丢弃不是shell变量名的环境变量名。来自the relevant standard

  

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

此外,定义了有效的shell名称is well-defined

  

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

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

... the POSIX specification for 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"