首页 > 解决方案 > 如何在bash中获取命令不同部分的退出代码

问题描述

假设我的 bash 脚本中有一行,ssh bad@location "find -name 'fruit.txt' | grep "Apple"我正在尝试检索, 和 "grep "Apple`的退出代码ssh,以查看哪个命令出错。find . -name 'fruit.txt'

到目前为止,我已经尝试过类似的东西echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]},但看起来$?返回的结果与本例中的相同${PIPESTATUS[0]}。我只需要返回第一个非零退出代码以及 dmesg 以进行调试。

我也考虑过使用set -o pipefail,如果有任何命令错误,它将返回失败退出代码,但我想知道哪个命令调试失败。

我想获得 255 的退出代码(来自 ssh)及其相应的 dmesg,或者以某种方式获得所有退出代码。

标签: bash

解决方案


ssh只向调用外壳返回一个退出状态(每个通道);如果要获取远程运行的各个管道组件的退出状态,则需要远程收集它们,将它们与数据一起放入,然后将它们解析出来。如果你有一个非常新版本的 bash,一种方法是这样的:

#!/usr/bin/env bash

# note <<'EOF' not just <<EOF; with the former, the local shell does not munge
# heredoc contents.
remote_script=$(cat <<'EOF'
  tempfile=$(mktemp "${TMPDIR:-/tmp}/output.XXXXXX"); mktemp_rc=$?
  find -name 'fruit.txt' | grep Apple >"$tempfile"
  printf '%s\0' "$mktemp_rc" "${PIPESTATUS[@]}"
  cat "$tempfile"
  rm -f -- "$tempfile"
  exit 0  # so a bad exit status will be from ssh itself
EOF
)

# note that collecting a process substitution PID needs bash 4.4!
exec {ssh_fd}< <(ssh bad@location "$remote_script" </dev/null); ssh_pid=$!
IFS= read -r -d '' mktemp_rc <&$ssh_fd   # read $? of mktemp
IFS= read -r -d '' find_rc <&$ssh_fd     # read $? of find
IFS= read -r -d '' grep_rc <&$ssh_fd     # read $? of grep
cat <&$ssh_fd                            # spool output of grep to our own output
wait "$ssh_pid"; ssh_rc=$?               # let ssh finish and read its $?

echo "mktemp exited with status $mktemp_rc" >&2
echo "find exited with status $find_rc" >&2
echo "grep exited with status $grep_rc" >&2
echo "ssh exited with status $ssh_rc" >&2

这是如何运作的?

  • exec {fd_var_name}< <(...)使用 bash 4.1自动文件描述符分配功能生成文件描述符编号,并将其与从进程替换运行中读取的内容相关联...
  • 在 bash 4.4 或更高版本中,还设置了进程替换$!,因此可以捕获它们的 PID,以便稍后wait为它们收集并收集它们的退出状态;这就是我们存储的内容ssh_pid
  • IFS= read -r -d '' varname从标准输入读取到下一个 NUL(在 中read -d '',第一个字符''被视为输入的结尾;作为 C 派生语言中的空字符串,字符串的第一个字节是它的 NUL 终止符)。

从理论上讲,这可以通过在退出状态值之前写入输出来变得更容易——这样你就不需要远程机器上的临时文件——但需要注意的是,如果find | grep输出中的任何地方都有 NUL,那么其中一些输出可以被reads 获取。(同样,您可以将输出存储在变量而不是临时文件中,但同样,这会破坏流输出中的任何 NUL)。


推荐阅读