如何在bash中防止嵌套函数失败被忽略

huangapple go评论99阅读模式
英文:

How to prevent nested function failures being missed in bash

问题

以下是代码的中文翻译:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function
  10. echo "父进程注意到子进程的退出码为 $?"
  11. }
  12. function grandparent_function {
  13. local ec
  14. ec=0 && parent_function || ec="$?"
  15. echo "祖父进程注意到父进程的退出码为 $ec"
  16. }
  17. grandparent_function

令人惊讶的是,上述代码的输出结果为:

  1. 父进程注意到子进程的退出码为 1
  2. 祖父进程注意到父进程的退出码为 0

将代码修改为:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function || return "$?"
  10. echo "父进程注意到子进程的退出码为 $?"
  11. }
  12. function grandparent_function {
  13. local ec
  14. ec=0 && parent_function || ec="$?"
  15. echo "祖父进程注意到父进程的退出码为 $ec"
  16. }
  17. grandparent_function

将返回预期结果:

  1. 祖父进程注意到父进程的退出码为 1

是否需要设置其他选项来修复这个问题?还是这是bash的一个错误?


我认为这是一个错误的原因是,将代码修改为不使用 || ec="$?" 将会遵守 -e/errexit 选项(如果命令以非零状态退出,则立即退出),并且每个失败的函数都会立即退出:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function
  10. echo "父进程注意到子进程的退出码为 $?"
  11. }
  12. function grandparent_function {
  13. parent_function
  14. echo "祖父进程注意到父进程的退出码为 $?"
  15. }
  16. grandparent_function

不会输出任何内容,并返回退出码 1。

英文:

The following code:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function
  10. echo "parent noticed child exit code as $?"
  11. }
  12. function grandparent_function {
  13. local ec
  14. ec=0 && parent_function || ec="$?"
  15. echo "grandparent noticed parent exit code as $ec"
  16. }
  17. grandparent_function

Will surprisingly output:

  1. parent noticed child exit code as 1
  2. grandparent noticed parent exit code as 0

Changing the code to:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function || return "$?"
  10. echo "parent noticed child exit code as $?"
  11. }
  12. function grandparent_function {
  13. local ec
  14. ec=0 && parent_function || ec="$?"
  15. echo "grandparent noticed parent exit code as $ec"
  16. }
  17. grandparent_function

Returns the expected result of:

  1. grandparent noticed parent exit code as 1

Is there an additional setting that I need to set to fix this? Or is this a bug in bash?


The reason I believe it is a bug, is that changing the code to not use the || ec="$?" will respect the -e/errexit option (Exit immediately if a command exits with a non-zero status) and have each failed function exit immediately:

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s huponexit
  4. shopt -s inherit_errexit
  5. function child_function {
  6. return 1
  7. }
  8. function parent_function {
  9. child_function
  10. echo "parent noticed child exit code as $?"
  11. }
  12. function grandparent_function {
  13. parent_function
  14. echo "grandparent noticed parent exit code as $?"
  15. }
  16. grandparent_function

Outputs nothing and returns exit code 1

答案1

得分: 1

感谢这些评论者:

还有这些支持资源:

我使用子shell技术为问题的代码示例创建了这个解决方法:

  1. #!/usr/bin/env bash
  2. set -e
  3. function child_function {
  4. return 1
  5. }
  6. function parent_function {
  7. child_function
  8. echo "parent noticed child exit code as $?"
  9. }
  10. function grandparent_function {
  11. local ec=0
  12. set +e; (set -e; parent_function); ec="$?"; set -e
  13. echo "grandparent noticed parent exit code as $ec"
  14. }
  15. grandparent_function

然而,子shell技术会阻止副作用,因为修改不会逃离子shell。

我花了几天时间研究了这个问题,并创建了一个包含各种技术的gist,其中包括一个可以处理副作用的方法:

https://gist.github.com/balupton/21ded5cefc26dc20833e6ed606209e1b

英文:

Thanks to the commenters:

And these supporting resources:

I created this workaround for the question's code sample using the subshell technique:

  1. #!/usr/bin/env bash
  2. set -e
  3. function child_function {
  4. return 1
  5. }
  6. function parent_function {
  7. child_function
  8. echo "parent noticed child exit code as $?"
  9. }
  10. function grandparent_function {
  11. local ec=0
  12. set +e; (set -e; parent_function); ec="$?"; set -e
  13. echo "grandparent noticed parent exit code as $ec"
  14. }
  15. grandparent_function

However, the subshell technique prevents side effects, as modifications do not escape the subshell.

I spent a few more days on this issue, and created a gist with various techniques, including one that works with side effects:

https://gist.github.com/balupton/21ded5cefc26dc20833e6ed606209e1b

huangapple
  • 本文由 发表于 2023年8月8日 23:40:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76861137.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定