获取后台进程的退出代码(Get exit code of a background process)

2019-08-18 05:19发布

我从我的主要Bourne shell脚本称为命令CMD是需要永远。

我想,如下所示修改脚本:

  1. 并联作为后台进程($ CMD&)运行命令CMD。
  2. 在主脚本,有一个循环监视催生命令每隔几秒钟。 循环也呼应了一些信息到stdout表示脚本的进度。
  3. 退出循环时,产生了命令终止。
  4. 捕获和报告生成的进程的退出代码。

有人可以给我指点做到这一点?

Answer 1:

1:在bash中, $! 认为被执行的最后一个后台进程的PID。 这会告诉你要监视的过程,反正。

4: wait <n>等待,直到与PID的进程<n>完成时(将阻塞,直到该过程完成,所以你可能不想打电话,直到你确信这个过程完成),然后返回退出代码已完成的过程。

2,3: psps | grep " $! " ps | grep " $! "可以告诉你该进程是否仍在运行。 它是由你如何理解输出,并决定它是整理的距离。 ( ps | grep不是傻瓜型如果你有时间,你能拿出一个更强大的方式来告诉进程是否仍在运行)。

这里有一个骨架脚本:

# simulate a long process that will have an identifiable exit code
(sleep 15 ; /bin/false) &
my_pid=$!

while   ps | grep " $my_pid "     # might also need  | grep -v grep  here
do
    echo $my_pid is still in the ps output. Must still be running.
    sleep 3
done

echo Oh, it looks like the process is done.
wait $my_pid
# The variable $? always holds the exit code of the last command to finish.
# Here it holds the exit code of $my_pid, since wait exits with that code. 
my_status=$?
echo The exit status of the process was $my_status


Answer 2:

这是我如何解决它的时候我也有类似的需要:

# Some function that takes a long time to process
longprocess() {
        # Sleep up to 14 seconds
        sleep $((RANDOM % 15))
        # Randomly exit with 0 or 1
        exit $((RANDOM % 2))
}

pids=""
# Run five concurrent processes
for i in {1..5}; do
        ( longprocess ) &
        # store PID of process
        pids+=" $!"
done

# Wait for all processes to finnish, will take max 14s
for p in $pids; do
        if wait $p; then
                echo "Process $p success"
        else
                echo "Process $p fail"
        fi
done


Answer 3:

当我看到几乎所有的答案使用外部工具(主要是ps )轮询后台进程的状态。 还有一个更unixesh解决方案,捕捉SIGCHLD信号。 在信号处理程序也必须检查其子进程停止。 它可以这样做kill -0 <PID>内置(通用)或检查存在/proc/<PID>目录(Linux特有的)或使用jobs内置( bash中特有的。 jobs -l还报告。PID在这种情况下,输出的第3场可以停止|运行|完成|退出)。

这里是我的榜样。

启动的过程被称为loop.sh 。 它接受-x或数作为参数。 对于-x是,退出代码1.对于一个数它等待NUM *5秒退出。 在每隔5秒就打印其PID。

启动过程被称为launch.sh

#!/bin/bash

handle_chld() {
    local tmp=()
    for((i=0;i<${#pids[@]};++i)); do
        if [ ! -d /proc/${pids[i]} ]; then
            wait ${pids[i]}
            echo "Stopped ${pids[i]}; exit code: $?"
        else tmp+=(${pids[i]})
        fi
    done
    pids=(${tmp[@]})
}

set -o monitor
trap "handle_chld" CHLD

# Start background processes
./loop.sh 3 &
pids+=($!)
./loop.sh 2 &
pids+=($!)
./loop.sh -x &
pids+=($!)

# Wait until all background processes are stopped
while [ ${#pids[@]} -gt 0 ]; do echo "WAITING FOR: ${pids[@]}"; sleep 2; done
echo STOPPED

更多解释见: 起价bash脚本失败的过程



Answer 4:

#/bin/bash

#pgm to monitor
tail -f /var/log/messages >> /tmp/log&
# background cmd pid
pid=$!
# loop to monitor running background cmd
while :
do
    ps ax | grep $pid | grep -v grep
    ret=$?
    if test "$ret" != "0"
    then
        echo "Monitored pid ended"
        break
    fi
    sleep 5

done

wait $pid
echo $?


Answer 5:

一转到后台子进程的PID存储在$! 。 您可以将所有的子进程的PID存储到一个数组,如PIDS []。

wait [-n] [jobspec or pid …]

等到通过JOBSPEC退出每个进程ID PID或作业规范中指定的子进程,并返回退出状态的最后一个命令等待。 如果作业规范给出,作业中的所有进程都在等待。 如果没有给出参数,所有当前活动的子进程等待,返回状态是零。 如果-n选项提供,等待等待任何工作终止,并返回它的退出状态。 如果没有JOBSPEC也不PID指定外壳的活动的子进程,返回状态是127。

使用等待命令,你可以等待所有子进程结束,同时你就可以用$每个子进程的退出状态 和存储状态到STATUS []。 然后,你可以做不同的状态的东西。

我曾尝试以下两种溶液,它们运行良好。 solution01更简洁,同时solution02有点复杂。

solution01

#!/bin/bash

# start 3 child processes concurrently, and store each pid into array PIDS[].
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
  ./${app} &
  PIDS+=($!)
done

# wait for all processes to finish, and store each process's exit code into array STATUS[].
for pid in ${PIDS[@]}; do
  echo "pid=${pid}"
  wait ${pid}
  STATUS+=($?)
done

# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
  if [[ ${st} -ne 0 ]]; then
    echo "$i failed"
  else
    echo "$i finish"
  fi
  ((i+=1))
done

solution02

#!/bin/bash

# start 3 child processes concurrently, and store each pid into array PIDS[].
i=0
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
  ./${app} &
  pid=$!
  PIDS[$i]=${pid}
  ((i+=1))
done

# wait for all processes to finish, and store each process's exit code into array STATUS[].
i=0
for pid in ${PIDS[@]}; do
  echo "pid=${pid}"
  wait ${pid}
  STATUS[$i]=$?
  ((i+=1))
done

# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
  if [[ ${st} -ne 0 ]]; then
    echo "$i failed"
  else
    echo "$i finish"
  fi
  ((i+=1))
done


Answer 6:

我会稍微改变你的方法。 而不是每隔几秒钟检查是否命令仍然活着,并且报告的消息,还有一个过程,报告每隔几秒钟,该命令仍在运行,然后杀死进程的命令完成时。 例如:

#!/bin/sh

cmd() { sleep 5; exit 24; }

cmd &   # Run the long running process
pid=$!  # Record the pid

# Spawn a process that coninually reports that the command is still running
while echo "$(date): $pid is still running"; do sleep 1; done &
echoer=$!

# Set a trap to kill the reporter when the process finishes
trap 'kill $echoer' 0

# Wait for the process to finish
if wait $pid; then
    echo "cmd succeeded"
else
    echo "cmd FAILED!! (returned $?)"
fi


Answer 7:

我们的团队曾与这在25分钟内未进行超时远程SSH执行的脚本同样需要。 下面是与监控循环检查后台进程每一秒,但仅打印每10分钟以抑制不活动超时的溶液。

long_running.sh & 
pid=$!

# Wait on a background job completion. Query status every 10 minutes.
declare -i elapsed=0
# `ps -p ${pid}` works on macOS and CentOS. On both OSes `ps ${pid}` works as well.
while ps -p ${pid} >/dev/null; do
  sleep 1
  if ((++elapsed % 600 == 0)); then
    echo "Waiting for the completion of the main script. $((elapsed / 60))m and counting ..."
  fi
done

# Return the exit code of the terminated background process. This works in Bash 4.4 despite what Bash docs say:
# "If neither jobspec nor pid specifies an active child process of the shell, the return status is 127."
wait ${pid}


Answer 8:

一个简单的例子,类似于上面的解决方案。 这不需要任何监控过程输出。 下一个例子使用尾跟随输出。

$ echo '#!/bin/bash' > tmp.sh
$ echo 'sleep 30; exit 5' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh &
[1] 7454
$ pid=$!
$ wait $pid
[1]+  Exit 5                  ./tmp.sh
$ echo $?
5

使用尾部跟随过程输出,当该过程完成后退出。

$ echo '#!/bin/bash' > tmp.sh
$ echo 'i=0; while let "$i < 10"; do sleep 5; echo "$i"; let i=$i+1; done; exit 5;' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh
0
1
2
^C
$ ./tmp.sh > /tmp/tmp.log 2>&1 &
[1] 7673
$ pid=$!
$ tail -f --pid $pid /tmp/tmp.log
0
1
2
3
4
5
6
7
8
9
[1]+  Exit 5                  ./tmp.sh > /tmp/tmp.log 2>&1
$ wait $pid
$ echo $?
5


Answer 9:

另一种解决方案是监测通过proc文件系统(除PS / grep的组合更安全的)过程; 当你启动一个进程它在/ proc / $ PID相应的文件夹,因此该解决方案可能是

#!/bin/bash
....
doSomething &
local pid=$!
while [ -d /proc/$pid ]; do # While directory exists, the process is running
    doSomethingElse
    ....
else # when directory is removed from /proc, process has ended
    wait $pid
    local exit_status=$?
done
....

现在你可以使用$ EXIT_STATUS变量,只要你喜欢。



Answer 10:

这可能超出你的问题,但是如果你担心的时间进程正在运行的长度,你可能有兴趣在一个时间间隔后检查正在运行的后台进程的状态。 这是很容易检查哪些孩子PID的使用仍在运行pgrep -P $$ ,但我想出了以下解决方案来检查已经过期的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为一个字符串变量,而不是如果你喜欢阵列把事情简单化。



Answer 11:

通过这种方法,你的脚本不必须等待后台进程,你将只需要监控的退出状态的临时文件。

FUNCmyCmd() { sleep 3;return 6; };

export retFile=$(mktemp); 
FUNCexecAndWait() { FUNCmyCmd;echo $? >$retFile; }; 
FUNCexecAndWait&

现在,你的脚本可以做任何事情,而你只需要继续监测retFile的内容(也可以包含类似的退出时间,你想要的任何其他信息)。

PS .:顺便说一句,我编写的思维在bash



文章来源: Get exit code of a background process