获取后台进程的退出代码

bob*_*bob 112 unix linux shell process

我有一个命令CMD从我的主要bourne shell脚本调用,需要永远.

我想修改脚本如下:

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

有人能指点我完成这个吗?

mob*_*mob 110

1:在bash中,$!保存已执行的最后一个后台进程的PID.无论如何,这将告诉您要监控的流程.

4:wait <n>等待ID完成的进程完成(它将阻塞直到进程完成,所以你可能不想在确定进程完成之前调用它).后<n>返回时,进程的退出代码在变量返回ps

2,3: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
Run Code Online (Sandbox Code Playgroud)

  • `kill -0 $!`是告诉进程是否仍在运行的更好方法.它实际上并不发送任何信号,只使用内置shell而不是外部进程来检查进程是否处于活动状态.正如`man 2 kill`所说,"如果_sig_为0,则不会发送任何信号,但仍会执行错误检查;这可用于检查是否存在进程ID或进程组ID." (45认同)
  • 如果您没有权限向正在运行的进程发送信号,@ ephemient`kill -0`将返回非零值.不幸的是,在这种情况下以及进程不存在的情况下它返回"1".这使得它很有用*除非*你没有拥有这个过程 - 如果涉及像sudo`这样的工具或者如果它们是setuid(并且可能是drop privs),那么即使对于你创建的进程也是如此. (13认同)
  • `ps -p $ my_pid -o pid =`既不需要`grep`. (12认同)
  • `wait`不会返回变量`$?`中的退出代码.它只返回退出代码,`$?`是最新前台程序的退出代码. (11认同)
  • 对于很多人投票'kill -0`.这是来自SO的同行评审参考,显示CraigRinger的评论是合法的:[`kill -0`将为正在运行的进程返回非零...但是对于任何正在运行的进程,`ps -p`将始终返回0](http ://stackoverflow.com/a/15774758/52074). (5认同)

Bjo*_*orn 45

这就是我遇到类似需求时的解决方法:

# 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
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案不满足要求#2:每个后台进程的监视循环.`wait`s cause脚本要等到(每个)进程结束. (3认同)

Abu*_*qil 7

#/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 $?
Run Code Online (Sandbox Code Playgroud)

  • 为什么每个人都忘记了`kill -0 $ pid`?它实际上并不发送任何信号,只使用内置shell而不是外部进程来检查进程是否处于活动状态. (5认同)
  • 因为你只能杀死你拥有的进程:`bash:kill:(1) - 不允许操作 (3认同)
  • 这是一个避免`grep -v`的技巧.你可以将搜索限制在行的开头:`grep'^'$ pid`另外,你可以做`ps p $ pid -o pid =`.另外,`tail -f`在你杀掉它之前不会结束,所以我认为这不是一个很好的演示方法(至少没有指出这一点).您可能希望将`ps`命令的输出重定向到`/ dev/null`,否则它将在每次迭代时转到屏幕.你的`exit`导致`wait`被跳过 - 它应该是一个`break`.但是不是'while` /`ps`和`wait`冗余? (2认同)
  • 循环是多余的.等一下 更少的代码=>更少的边缘情况. (2认同)

Tru*_*ueY 7

我看到几乎所有答案都使用外部工具(大多数ps)来轮询后台进程的状态.有一个更多的unixesh解决方案,捕获SIGCHLD信号.在信号处理程序中,必须检查哪个子进程已停止.它可以通过kill -0 <PID>内置(通用)或检查/proc/<PID>目录的存在(特定jobs于Linux)或使用内置(特定于.jobs -l还报告pid来完成.在这种情况下,输出的第3个字段可以是Stopped | Running |完成|退出.).

这是我的例子.

调用已启动的进程loop.sh.它接受-x或数字作为参数.对于-x具有退出代码1的退出.对于数字,它等待数*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
Run Code Online (Sandbox Code Playgroud)

有关更多说明,请参阅:从bash脚本启动进程失败

  • 由于我们讨论的是 Bash,因此 for 循环可以使用参数扩展编写为:`for i in ${!pids[@]};`。 (2认同)

Ter*_*ang 6

背景子进程的pid存储在$!中!.您可以将所有子进程的pid存储到数组中,例如PIDS [].

wait [-n] [jobspec or pid …]
Run Code Online (Sandbox Code Playgroud)

等到每个进程ID pid或作业规范jobspec指定的子进程退出并返回等待的最后一个命令的退出状态.如果给出了作业规范,则等待作业中的所有进程.如果未给出参数,则等待所有当前活动的子进程,并且返回状态为零.如果提供了-n选项,则wait等待任何作业终止并返回其退出状态.如果jobspec和pid都没有指定shell的活动子进程,则返回状态为127.

使用wait命令可以等待所有子进程完成,同时你可以通过$获得每个子进程的退出状态并将状态存储到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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • 请阅读“[如何写出一个好的答案?](https://stackoverflow.com/help/how-to-answer)”,您将在其中找到以下信息:**...尝试提及任何限制、答案中的假设或简化。简洁是可以接受的,但更全面的解释会更好。** 因此,您的回答是可以接受的,但如果您能详细说明问题和解决方案,那么您获得投票的机会会更大。:-) (2认同)
  • `pid=$!; PIDS[$i]=${pid}; ((i+=1))` 可以更简单地写成`PIDS+=($!)`,它只是简单地附加到数组中,而无需使用单独的变量进行索引或 pid 本身。同样的事情也适用于 `STATUS` 数组。 (2认同)

Wil*_*ell 5

我会稍微改变您的方法。而不是每隔几秒钟检查一次命令是否仍在运行并报告消息,而是让另一个进程每几秒钟报告一次该命令仍在运行,然后在命令完成时终止该进程。例如:

#!/ bin / sh

cmd(){睡眠5; 出口24; }

cmd&#运行长时间运行的进程
pid = $!#记录pid

#生成一个进程,最终报告该命令仍在运行
而回声“ $(日期):$ pid仍在运行”;睡1 完成&
echoer = $!

#设置陷阱以在过程结束时杀死报告程序
陷阱'kill $ echoer'0

#等待过程完成
如果等待$ pid; 然后
    回声“ cmd成功”
其他
    回声“ cmd FAILED !!(返回$?)”
科幻