获取后台进程的退出代码

177

我有一个在我的主Bourne Shell脚本中调用的命令CMD需要很长时间才能执行完。

我想按照以下方式修改脚本:

  1. 将命令CMD作为后台进程并行运行(CMD &)。
  2. 在主脚本中,有一个循环来监视每隔几秒钟生成的命令。该循环还会打印一些消息到标准输出,指示脚本的进度。
  3. 当生成的命令终止时退出循环。
  4. 捕获并报告生成的进程的退出代码。

有人能给我提供实现这个需求的提示吗?


14个回答

1
如何...
# run your stuff
unset PID
for process in one two three four
do
    ( sleep $((RANDOM%20)); echo hello from process $process; exit $((RANDOM%3)); ) & 2>&1
    PID+=($!)
done

# (optional) report on the status of that stuff as it exits
for pid in "${PID[@]}"
do
    ( wait "$pid"; echo "process $pid complemted with exit status $?") &
done

# (optional) while we wait, monitor that stuff
while ps --pid "${PID[*]}" --ppid "${PID[*]}" --format pid,ppid,command,pcpu
do
    sleep 5
done | xargs -i date '+%x %X {}'

# return non-zero if any are non zero
SUCCESS=0
for pid in "${PID[@]}"
do
    wait "$pid" && ((SUCCESS++)) && echo "$pid OK" || echo "$pid returned $?"
done

echo "success for $SUCCESS out of ${#PID} jobs"
exit $(( ${#PID} - SUCCESS ))

我认为 ${#PID} 应该改为 ${#PID[@]} - Troels Ynddal

1
我的解决方案是使用匿名管道将状态传递给监控循环。没有使用临时文件来交换状态,因此没有需要清理的内容。如果您对后台作业的数量不确定,中断条件可以是[ -z "$(jobs -p)" ]
#!/bin/bash

exec 3<> <(:)

{ sleep 15 ; echo "sleep/exit $?" >&3 ; } &

while read -u 3 -t 1 -r STAT CODE || STAT="timeout" ; do
    echo "stat: ${STAT}; code: ${CODE}"
    if [ "${STAT}" = "sleep/exit" ] ; then
        break
    fi
done

0
这可能超出了您的问题范围,但是如果您担心进程运行的时间过长,您可能会有兴趣在一段时间间隔后检查正在运行的后台进程的状态。使用pgrep -P $$很容易检查哪些子PID仍在运行,但我想出了以下解决方案来检查已经过期的那些PID的退出状态:
cmd1() { sleep 5; exit 24; }
cmd2() { sleep 10; exit 0; }

pids=()
cmd1 & pids+=("$!")
cmd2 & pids+=("$!")

lasttimeout=0
for timeout in 2 7 11; do
  echo -n "interval-$timeout: "
  sleep $((timeout-lasttimeout))

  # you can only wait on a pid once
  remainingpids=()
  for pid in ${pids[*]}; do
     if ! ps -p $pid >/dev/null ; then
        wait $pid
        echo -n "pid-$pid:exited($?); "
     else
        echo -n "pid-$pid:running; "
        remainingpids+=("$pid")
     fi
  done
  pids=( ${remainingpids[*]} )

  lasttimeout=$timeout
  echo
done

输出结果为:

interval-2: pid-28083:running; pid-28084:running; 
interval-7: pid-28083:exited(24); pid-28084:running; 
interval-11: pid-28084:exited(0); 

注意:如果您愿意,可以将$pids更改为字符串变量而不是数组,以简化事情。

0

如果您只想并行运行固定数量的命令,并确保错误不被忽略,您可以选择以下非常简单的选项:

#!/bin/bash

set -e

python3 -c "import time; import sys; time.sleep(1); sys.exit(1)" &
python3 -c "import time; import sys; time.sleep(3); sys.exit(0)" &

wait -n
wait -n

wait -n 等待下一个作业完成并返回其退出码。由于我们使用了 set -e,它将导致整个脚本失败。

请注意,它仍然会让其他作业在后台运行。如果你不希望这样,可以尝试以下方法:

{ wait -n && wait -n ; } || { wait; exit 1; }

我认为如果你需要更复杂的东西,就不应该使用shell脚本。用Python或Deno来做会更好。

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