【问题标题】:bash caller builtin stops working from exported function, why?bash caller builtin 停止从导出的函数工作,为什么?
【发布时间】:2016-10-23 11:13:14
【问题描述】:

我在使用bash 和导出函数时遇到了非常奇怪的问题,以便为我对内置caller 的调用提供可靠的答案。

这是我的设置来说明这个问题:Bash 脚本bar 定义和导出函数bar1bar2bar2 致电 bar1。 Bash 脚本 bar 然后执行 bash 脚本 foo 将调用 bar1

只有在调用bar1 之后,调用者内置函数才会中断。这是正常的吗?你能解释一下为什么吗?你能简化以下暴露问题的代码吗?

为了清楚起见,这里是如何在您的系统上重现:构建两个文件:

cd /tmp
cat <<"EOF" > foo 
#!/bin/bash

bar1
EOF
chmod +x foo

cat <<"EOF" > bar 
#!/bin/bash

bar2() {
    echo "$FUNCNAME IN: $(caller 0)" >&2 
} 
export -f bar2

bar1() {
    echo "$FUNCNAME BEFORE: $(caller 0)" >&2
    bar2
    echo "$FUNCNAME AFTER: $(caller 0)" >&2 
}
export -f bar1

./foo
EOF
chmod +x bar

然后你可以摆弄看看:

$ ./bar
bar1 BEFORE: 3 main ./foo
bar2 IN: 
bar1 AFTER: 

我预计(行号有可接受的变化):

$ ./bar
bar1 BEFORE: 9 main ./foo
bar2 IN: 5 bar ./foo
bar1 AFTER: 9 main ./foo

最终,我的问题是:在所有情况下我怎样才能绕过这个问题并获得调用者

附加信息:

  • bash 版本:4.3.42(1)-release (x86_64-pc-linux-gnu) 来自 ubuntu 包 4.3-14ubuntu1

【问题讨论】:

  • 有趣的问题。我会注意到,如果您未能导出bar2,您会收到预期的错误./foo: line 1: bar2: command not found,但对caller 的最终调用确实会产生bar1 AFTER: 3 main ./foo,所以看起来(可能)调用导出的函数from 导出的函数对调用堆栈有一些影响。

标签: bash introspection built-in


【解决方案1】:

这是 bash 中的 bug。它已在 4.4 版中修复。

在存在导出函数的情况下,BASH_SOURCE 变量未正确维护。可以通过显示FUNCNAMEBASH_SOURCEBASH_LINENO特殊变量的内容来查看:

cd /tmp
cat <<"EOF" > foo 
#!/bin/bash

bar1 
EOF
chmod +x foo

cat <<"EOF" > bar 
#!/bin/bash

bar2() {
    echo "$FUNCNAME IN: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2 
} 
export -f bar2

bar1() {
    echo "$FUNCNAME BEFORE: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2
    bar2
    echo "$FUNCNAME AFTER: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2 
} 
export -f bar1

./foo
EOF
chmod +x bar

./bar的输出:

bar1 BEFORE: 3 main ./foo [bar1 main] [./foo] [3 0]
bar2 IN:  [bar2 bar1 main] [./foo] [1 3 0]
bar1 AFTER:  [bar1 main] [] [3 0]

如您所见,与调用导出函数相对应的堆栈帧不会添加到BASH_SOURCE,但每当函数返回时,最顶层的堆栈帧就会被弹出。

请注意,FUNCNAME 变量不受此错误的影响。因此,如果您只需要调用者的姓名,您可以将其作为${FUNCNAME[1]} 获取。

【讨论】:

  • 事实上,您给了我一个很好的答案,告诉我如何使用FUNCNAME[1] 来获得呼叫者(在我的问题末尾以粗体字询问)。如果您在 GNU bash (savannah.gnu.org/support/?108903) 中包含指向实际错误报告的链接,并清楚地告诉 Chet,这已在 4.4 中修复,我没有任何理由不接受您的回答 ;-)
猜你喜欢
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
  • 2019-05-30
相关资源
最近更新 更多