bash“catch block”没有捕获坏的子shell

bash“catch block”没有捕获坏的子shell

所以我有这个:

  (
        set -eo pipefail;
         {
          set -eo pipefail;
        file_path="$(echo "$i" | jq -r '.file_path')"

         if [[ -n "$file_path" ]]; then
              echo "$i" > "$file_path";
          fi
        } || {
           # never seems to reach here
           echo "!!! json parse error: 'xxxx'";
        }
     )

我希望在我的终端中出现这样的情况:

!!! json parse error: 'xxxx'

jq但我的终端中不断出现此错误:

parse error: Invalid numeric literal at line 2, column 0

不知道为什么通常对我有用的“catch block”在这里不起作用

答案1

中的shellpipefail选项bash导致管道的退出状态为最右侧命令的退出状态,且退出状态非零(如果所有命令成功退出,则退出状态为零)。在您的情况下,您拥有的唯一管道是echo+jq管道,并且echo不太可能失败,从而使该pipefail选项变得多余。

errexit( ) shell选项set -e导致 shell 在第一个返回非零退出状态的命令处终止,除非该命令是 AND-OR 列表的一部分,例如代码中的列表。

激发!!! json parse error: 'xxxx'代码输出的唯一方法是通过if复合命令返回非零退出状态,如果 shell 无法写入$file_path.

就我个人而言,我会避开这两个 shell 选项,除非您遇到绝对需要它们的情况(我在自己的 shell 脚本中还没有看到过其中一种情况)。


jq如果解析生成一些非空值,您似乎希望将 JSON 文档写入文件(由 的输出给出),并在解析失败时输出错误消息。

确定解析是否顺利可能最简单的方法是jq直接在if语句中使用退出状态。

在这里,我们另外捕获错误输出jq并在我们自己的错误报告中使用它:

if filepath=$( jq -r '.file_path' <<<"$json_document" 2>&1 )
then
    # Parsing went ok.
    if [ -n "$filepath" ]; then
        # "$filepath" is non-empty.
        printf '%s\n' "$json_document" >"$filepath"
    fi
else
    # Parsing failed.
    printf 'ERROR: "%s"\n' "$filepath" >&2
fi

但老实说,我只是让它jq自己进行错误报告,而根本不参与其中,这简化了代码:

if filepath=$( jq -r '.file_path' <<<"$json_document" ) && [ -n "$filepath" ]
then
    # Parsing went ok, and "$filepath" is non-empty.
    printf '%s\n' "$json_document" >"$filepath"
fi

或者,让jq我们告诉我们该值为空,这将消除测试-n

if filepath=$( jq -r -e '.file_path' <<<"$json_document" )
then
    # Parsing went ok, and "$filepath" is non-empty (and not null or false).
    printf '%s\n' "$json_document" >"$filepath"
fi

对于-e,jq将在出现错误时返回非零退出状态(像往常一样),但如果最终表达式产生空结果,null, 或也会返回false

答案2

我真的不明白为什么您会期望看到该消息。您正在测试一组命令的退出代码,这意味着您正在测试该组中最后一个命令的退出代码。在您的情况下,最后一个命令是if或者,如果是iftrue,则echo

  if [[ -n "$file_path" ]]; then
    echo is n
  fi

由于这是最后运行的命令,是您|| echo "!!! json parse error: 'xxxx'"测试的对象,并且由于执行的ifechoif 都会返回 true,因此您永远不会进入“catch 块”。

如果您希望由于该选项而执行此pipefail操作,则该操作将不起作用,因为这不是管道:

$ { false | true; } || echo FAILED
$ set -o pipefail 
$ { false | true; } || echo FAILED
FAILED

但是如果我现在在管道之后添加另一个无失败命令:

 $ { false | true; true;} || echo FAILED
 $ 

这就是你的脚本中的内容。

相关内容