【问题标题】:Loop over environment variables in POSIX sh在 POSIX sh 中循环环境变量
【发布时间】:2017-06-13 09:39:33
【问题描述】:

我需要遍历环境变量并在 POSIX sh(不是 bash)中获取它们的名称和值。这是我目前所拥有的。

#!/usr/bin/env sh

# Loop over each line from the env command
while read -r line; do
  # Get the string before = (the var name)
  name="${line%=*}"
  eval value="\$$name"

  echo "name: ${name}, value: ${value}"
done <<EOF
$(env)
EOF

它在大多数情况下都有效,除非环境变量包含换行符。我需要它在这种情况下工作。

我知道 env-0 标志用 nul 而不是换行符分隔变量,但如果我使用该标志,如何循环遍历每个变量? 编辑:@chepner 指出 POSIX env 不支持 -0,所以就这样了。

任何使用便携式 linux 实用程序的解决方案都很好,只要它在 POSIX sh 中工作即可。

【问题讨论】:

  • 假设POSIX env 根据需要生成\0 分隔输出,您打算如何使用脚本进行解析,POSIX read 不支持 de-lmiter @987654330 @ 也不是 -0 支持 xargs
  • 一般来说,你甚至不能假设name 是一个有效的变量名。运行 env "foo bar=3" 并检查输出。
  • POSIX env 无论如何都不支持 -0
  • @chepner 对无效的变量名很感兴趣。理想情况下,该解决方案将检查某些东西是否是有效变量(或以不同的方式获取名称)。此外,我编辑了我的问题以解决我在 -0 标志上的错误。

标签: unix environment-variables sh


【解决方案1】:

没有办法完全自信地解析env的输出;考虑这个输出:

bar=3
baz=9

我可以在两种不同的环境中制作它:

$ env -i "bar=3" "baz=9"
bar=3
baz=9
$ env -i "bar=3
> baz=9"
bar=3
baz=9

这两个环境变量barbaz是简单的数值,还是一个变量bar的值$'3\nbaz=9'(使用bash的ANSI引用风格)?


您可以使用 POSIX awk 安全地访问环境,但是,使用 ENVIRON 数组。例如:

awk 'END { for (name in ENVIRON) {
            print "Name is "name;
            print "Value is "ENVIRON[name];
           }
         }' < /dev/null

通过这个命令,你可以区分上面提到的两种环境。

$ env -i "bar=3" "baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is baz
Value is 9
Name is bar
Value is 3
$ env -i "bar=3
> baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is bar
Value is 3
baz=9

【讨论】:

  • 您的解决方案的工作原理是它会遍历变量而不会因换行符引起混淆,但是如果您想解析 awk 的输出以执行除在终端上打印之外的其他操作,您仍然有同样的问题(必须根据每行的开头猜测)。有没有办法让awk 输出的格式可以保证可以毫无歧义地解析它?
  • 我不建议解析awk(或语言X)的输出;我会做任何需要in awk(或语言X)的事情。避免歧义的唯一方法是使用结构化语言,如 XML、JSON、YAML 等,然后您会遇到两个问题:首先生成输出,然后解析 it得到它。这些都不是 POSIX 定义的工具的特殊优势。
  • 我经常感到惊讶的是,awk/sed 解决方案经常被呈现(和接受)为“shell”解决方案,而实际上是不同的编程语言。毫无疑问,这些工具在很大程度上得到了支持,并且通常可供使用 shell 的任何人使用。但是它们仍然保留在 shell 外部,如果你不能可靠地解析 shell 中工具的输出,那么你就会失去一些“一切都作为文件 + 享受管道的乐趣”的哲学,而这种哲学实际上得到了 sed/awk 的考虑“shell 语法”开始。哦!嗯...
  • “任何使用便携式 linux 实用程序的解决方案都很好,只要它在 POSIX sh 中工作。” 当它们被保证时使用外部实用程序没有任何问题,因为它们就在这里。
  • 这并不是对您提出的解决方案的批评或特定于 OP 问题的评论,只是一般认为您的解决方案提供了外壳无法提供的东西,因此它在上下文中肯定是相关的。
【解决方案2】:

也许这行得通?

#!/usr/bin/env sh
env | while IFS= read -r line
do
  name="${line%%=*}"
  indirect_presence="$(eval echo "\${$name+x}")"
  [ -z "$name" ] || [ -z "$indirect_presence" ] || echo "name:$name, value:$(eval echo "\$$name")"
done

这不是万无一失的,好像带有换行符的变量的值恰好有一个看起来像赋值的行开头,这可能会有些混乱。

扩展使用%% 删除最长匹配,因此如果一行包含多个= 符号,则应将它们全部删除以仅保留行首的变量名。

【讨论】:

  • 间接参数扩展是bash 扩展,POSIX 不支持。
  • @chepner 我已经修改了我的答案。你现在有什么不对劲的地方吗?
  • 该问题要求处理包含换行符的值的解决方案;这没有。使用env 的输出无法做到这一点;以我的回答为例。
  • @chepner 我想你还没有完全看过我的代码。它处理值中的换行符,因为该值是从变量中重新读取的,而不是从env 的输出中解析的。也许这还有其他一些缺点,但我检查了换行符的值。
  • 真;鉴于您确实提到了警告,我将撤销反对票,但我认为这不是一个好方法。
【解决方案3】:

这里是一个基于 awk 方法的示例:

#!/bin/sh
for NAME in $(awk "END { for (name in ENVIRON) { print name; }}" < /dev/null)
do
  VAL="$(awk "END { printf ENVIRON[\"$NAME\"]; }" < /dev/null)"

  echo "$NAME=$VAL"
done

【讨论】:

  • 根据 shellcheck (SC2013) 应该用 while 替换 for 循环。这会将您的第一行更改为:awk "END { for (name in ENVIRON) { print name; }}" &lt; /dev/null | while IFS= read -r NAME
猜你喜欢
  • 2012-06-06
  • 2011-02-25
  • 1970-01-01
  • 2013-02-14
  • 2015-04-30
  • 2019-09-24
  • 2014-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多