Bash脚本在从函数返回后意外退出

3
这是我对stackoverflow上上一个问题的跟进。我将脚本缩减至必要部分,但如果有人认为了解脚本功能可能会有帮助,可以查看其他问题。
 #!/usr/bin/env bash
 set -eu -o pipefail

 declare -a framelist


 #Print all results

 function output_values() {
   echo "Results!";
 }

 #parsing information from stdin

 function parser () {

   while read tc;
     do

      if [ -z "$tc" ]; then
        continue
      fi

      #Evaluation and saving result to array

      echo $result_value;

      framelist+=($result_value);

      if (( <<some abort condition>> )); then
        exec 0>&-
        echo "Last result: $result_value";
        return 0
      fi

     done
 }

 some_command_writing_to_stdout | parser $2;
 output_values;

该脚本执行命令并将输出传输到我的本地函数,最终在echo "Last result: $result_value";这一行返回结果。此后,它将终止提供数据的命令,该数据在此函数中被解析 - 这也可以正常工作。
当达到return 0时,我认为脚本的下一行(命令正下方的那一行)output_values;应该被执行,但实际上没有被执行。
即使在解析器函数中的打印结果的echo行之前直接调用output_values函数,它也不会被执行。
更奇怪的是,我可以注释掉exec 0>&-,所有的行为都和原来一样。即使应该由此行终止的命令在解析器函数退出时也会被终止。
我需要改变什么才能在解析器函数返回后继续处理其结果?这不可能是预期的行为。
问候
Manuel

发布一个简单、可运行且能够产生精确错误的示例代码。 - Anubis
你尝试过使用 set -x 吗? - Nitinkumar Ambekar
set -x 只是显示在 return 0 后退出,之后不执行任何操作。 - M. Uster
1个回答

5
让我们看一下man bash中关于pipefail的部分:

pipefail

如果设置了该选项,则管道的返回值为最后一个(最右边)具有非零状态退出的命令的值,如果管道中的所有命令都成功退出,则返回值为零。默认情况下,此选项被禁用。

结合使用set -e,每当命令(管道)以非零退出状态退出时,将退出,唯一的逻辑结论是:您的某些输出到标准输出的命令必须以非零退出状态退出(因为显然,解析器0退出)。
这就解释了为什么管道中的下一个命令(parser)会被执行,以及为什么您的脚本在此之后结束。
很容易验证这一点。只需将倒数第二条语句替换为:
(some_command_writing_to_stdout || true) | parser $2

你是完全正确的。我注释掉了pipefail选项,现在它可以工作了。当使用set -x时,我本来期望能够得到关于退出代码!=0导致脚本立即退出的信息。 - M. Uster
你没有得到“some_command_writing_to_stdout”的退出代码吗? - randomir
该脚本输出从解析管道stdout数据和“last result”行获取的值。使用set -x并不能提供比执行的最后一行是return 0更多的信息。 - M. Uster
我猜你的 some_command_writing_to_stdout 可能是一个函数(那么你应该能看到最后执行的 return 语句),但如果它是一个命令,那么你是对的,你将无法使用 set -x 看到退出状态。这很不幸。 - randomir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接