【问题标题】:bash 4.4 not passing variables to sub-shellbash 4.4没有将变量传递给子shell
【发布时间】:2017-05-18 13:55:51
【问题描述】:

Bash 4.4 和 4.3 似乎在将变量传递到子 shell 或 heredocs 方面表现不同。

来自@CharlesDuffy 的简单复制:

export var=0; var=1 cat <<<"$(env | grep '^var')"

Bash 4.4 输出var=0,bash 4.3 输出var=1

我用于重现问题的原始脚本:

TZ=Europe/London
timezone=Asia/Tokyo
echo TZ=$TZ
echo timezone=$timezone

date +%H:%M
TZ=$timezone date +%H:%M

IFS=':' TZ=$timezone read hour minute <<EOF
        $(date +%H:%M)
EOF
echo TZOUTER $hour-$minute

IFS=':' read hour minute <<EOF
        $(TZ=$timezone date +%H:%M)
EOF
echo TZINNER $hour-$minute

Bash 4.3 (Ubuntu 16.04) 提供:

TZ=Europe/London
timezone=Asia/Tokyo
14:52
22:52
TZOUTER 22-52
TZINNER 22-52

Bash 4.4 (Ubuntu 17.04) 提供:

TZ=Europe/London
timezone=Asia/Tokyo
13:53
22:53
TZOUTER 13-53
TZINNER 22-53

(在 bash 4.3 上,内部和外部方法给出相同的时间,在 bash 4.4 上,TZOUTER 给出原始 TZ 值)。

有人知道为什么会这样吗?我查看了https://lists.gnu.org/archive/html/info-gnu/2016-09/msg00008.html,但无法确定是哪个更改导致了行为改变。

【问题讨论】:

  • 明确地说,bash 4.4 的行为让我觉得更正确。 TZ=$timezone read ... 应该只影响 read 命令的实际执行。它不会影响命令参数的扩展,那么为什么期望它会影响 heredoc 是合理的呢?
  • 顺便说一句,您是否考虑构建一个更简单的复制器?这需要相当多的解释。我建议将var=0; var=1 cat &lt;&lt;&lt;"$var" 作为一个简明扼要地展示行为差异的单行符。
  • 参见,例如,stackoverflow.com/questions/33476446/…bash 有几个关于环境修饰符的错误,这里的文档/字符串已在过去几个版本中得到修复。
  • 相比之下,export var=0; var=1 cat &lt;&lt;&lt;"$(env | grep '^var')" 确实在 4.3 中重现。
  • 当然是行为上的改变;您的代码取决于一个错误,并且该错误已得到修复。从未记录过命令修饰符会影响参数或重定向到命令,只有发生命令查找和执行的环境。

标签: linux bash shell


【解决方案1】:

来自 2014-12-3 的详细变更日志条目:

subst.c

  • command_substitute: 如果运行命令替换作为 扩展重定向(expanding_redir == 1),刷新任何临时 我们作为该命令的一部分继承的环境,因为我们不是 应该可以访问临时环境。自从 expanding_redir 仅控制对临时环境的访问 变量查找和绑定,我们可以在子shell中关闭它

具体增加如下新代码:

  if (expanding_redir)
    {
      flush_temporary_env ();
      expanding_redir = 0;
    }

...简单地删除 temporary_env 哈希表的所有内容,防止在与重定向相关的扩展期间在 execute_simple_command() 中调用 merge_temporary_env() 时不适当地扩展临时环境变量。

【讨论】:

    【解决方案2】:

    这发生了变化,因为bash 4.3 错误地将 precommand 修饰符应用于此处文档中命令替换的评估。在

    IFS=':' TZ=$timezone read hour minute <<EOF
    $(date +%H:%M)
    EOF
    

    IFSTZ 的更改应该read 命令本身可见,而不是date 命令。以上应该与

    dateStr=$(date +%H:%M)
    IFS=':' TZ=$timezone read hour minute <<EOF
    $dateStr
    EOF
    

    此错误已在bash 4.4 中修复,我怀疑这是对与此处文档和此处字符串的评估相关的代码的一般审计的一部分。 4.3 中修复了一些相关的错误,但其他错误仍然存​​在。

    【讨论】:

      【解决方案3】:

      两种环境的区别可能在于TZ是否导出

      (
          export TZ
          IFS=':' TZ=$timezone read hour minute <<EOF
              $(date +%H:%M)
      EOF
          echo TZOUTER $hour-$minute
      )
      
      (
          IFS=':' TZ=$timezone read hour minute <<EOF
              $(date +%H:%M)
      EOF
          echo TZOUTER $hour-$minute
      )
      

      编辑 为了避免 here-doc 中的变量扩展,请在 EOF 周围添加单引号

      IFS=':' TZ=$timezone read hour minute <<'EOF'
          $(date +%H:%M)
      EOF
      

      但是这里预计是扩展shell命令,所以最清楚

      (
          TZ=$timezone 
          IFS=':' read hour minute <<EOF
              $(date +%H:%M)
      EOF
      )
      

      另一个比较模棱两可,因为参数适用于读取命令,但不清楚是否应该适用于heredoc替换

      【讨论】:

      • 我已将脚本更改为在顶部导出 TZ,但我仍然看到了差异。
      • 不是出口的问题——我们知道是出口的;这是一个使用哪个值导出的问题,以及两个 bash 版本之间的哪些具体变化改变了这种行为。
      • 是的,后来看到了,我只是用 export 和没有的情况进行了测试,可能会看到不同的结果,但这可能是这里的一个错误-doc
      猜你喜欢
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 2013-03-28
      • 2021-12-12
      • 2017-02-22
      • 1970-01-01
      相关资源
      最近更新 更多