【问题标题】:Get exit status of background processes in bash在bash中获取后台进程的退出状态
【发布时间】:2018-08-10 22:39:21
【问题描述】:

我尝试了多种不同的方法来检索后台进程的退出状态:

  1. 捕获每个后台进程的pid,存储在数组中然后等待每个PID,获取每个PID的返回状态并存储在STATUS数组中。

Con: pid 不是这个 shell 的子代

  1. tail --pid= -f /dev/null

Con:此处的退出状态始终为 0

在 stackoverflow 上四处寻找各种答案。我仍然无法让它工作。你能帮忙告诉我哪里出错了吗?

PIDS=()
STATUS=()
OVERALL_EXIT=0

# run processes and store pids in array

for target in ${target_list} ; do
    ./<script_to_execute> ${target} &
    PIDS+=$!
done


# wait for all processes to finish and then capture return status of each
for pid in ${PIDS[@]}; do
    echo "${pid}"
    wait ${pid} 
    #tail —pid=${pid} -f /dev/null
    #ps ax | grep ${pid} | grep -v grep 
    STATUS+=($?)
done

# looping through the status arr to check exit code for each
i=0
for st in ${STATUS[@]}; do
    if [[ ${st} -ne 0 ]]; then
        echo "$i failed"
        OVERALL_EXIT=1
    else
        echo "$i finished"
    fi
    ((i+=1))
done

exit ${overall_exit}

【问题讨论】:

  • PIDS+=( "$!" )。如果不使用括号,则将字符附加到列表的第一个元素,而不是添加额外的列表元素。并使用for pid in "${PIDS[@]}"带引号;通过shellcheck.net 运行您的代码
  • 顺便说一句,这不是您的问题的原因,但请注意,全大写名称用于对 shell 有意义的变量,而带有小写字符的名称保证不会与 shell 的行为冲突。来自 POSIX 规范 @pubs.opengroup.org/onlinepubs/9699919799/basedefs/… -- 包含小写字母的环境变量名称的命名空间是为应用程序保留的。应用程序可以使用此名称空间中的名称定义任何环境变量,而无需修改标准实用程序的行为。
  • ...阅读上述内容时,请记住,即使设置常规 shell 变量也会覆盖任何已存在的同名环境变量。
  • 您现有的 wait "$pid" 方法很好,如果您只是修复存储和检索 PID 的方式(正如我的回答向您展示的那样)。也许可以让它wait "$pid" || (( overall_exit |= $? )) 来同时更新一个overall_exit 变量,而不需要第二个循环。
  • 让我再次强调一下 -- 以使先前答案无效的方式更改问题是违反这里的规则的。如果其他人无法理解并从问题中学习他们看不到答案的上下文。(见meta.stackoverflow.com/questions/295089/…

标签: bash parallel-processing background-process exit-code


【解决方案1】:
PIDS+=$!

...没有做你认为它做的事。考虑:

PIDS=( )
PIDS+=11
PIDS+=22
PIDS+=33
declare -p PIDS

...如果您期望输出的是:

declare -a PIDS='([0]="11" [1]="22" [2]="33")

...你错了,因为它实际上发出的是:

declare -a PIDS='([0]="112233")'

...因为+= 仅当右侧的东西是数组时才追加一个新的数组元素。

因此,您会收到 not a child of this shell 错误,因为将所有 PID 连接到一个字符串中的结果并不是实际存在的 PID。

要修复它,请使用括号:PIDS+=( "$!" )


提供一个端到端的例子:

#!/usr/bin/env bash

# run four different processes; two exit status 0, one exits status 1, on exits status 2
# ...exits happen at delays ranging between 2-5 seconds.
delays=( 5 3 2 4 )
exits=(  0 0 1 2 )
for idx in "${!delays[@]}"; do
  (sleep "${delays[$idx]}"; exit "${exits[$idx]}") &
  pids+=( "$!" )
done

exit_status=0
for pid in "${pids[@]}"; do
  wait "$pid"; (( exit_status |= $? ))
done
echo "Combined exit status is $exit_status"
exit "$exit_status"

...5 秒后正确退出:

Combined exit status is 3

【讨论】:

  • 非常感谢,按照您的建议尝试一下,很快就会让您知道结果:)
  • 试过了,但仍然收到一条消息:pid 不是这个 shell 的子进程,并注意所有进程都以代码 127 退出。
  • 我在此处提供的确切代码是否会发生这种情况,复制并粘贴(到脚本,而不是交互式外壳)而不做任何更改?当我们没有创建它的代码时,我们无法解决问题。
  • 使用上面的示例代码,让我看看我做错了什么。非常感谢您的帮助!此示例代码使我缩小了问题范围!并且也明白我哪里出错了..
  • 好吧,经过数小时的调试发现我有一个额外的等待,然后才添加等待每个 pid。难怪以前无法运行它!非常感谢@Charles
【解决方案2】:

(这应该是注释,但代码格式错误)。

查看您的完整代码,您似乎正在尝试在 bash 中实现基本版本的 GNU Parallel。

parallel -j0 ./<script_to_execute> ::: ${target_list}

我相信这将与完整代码一样(即,如果其中一项作业失败,则返回错误)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    相关资源
    最近更新 更多