linux 如何在给定的超时后杀死Bash中的子进程?

myss37ts  于 2023-04-05  发布在  Linux
关注(0)|答案(9)|浏览(170)

我有一个bash脚本,它启动一个子进程,该进程会不时地崩溃(实际上是挂起),而且没有明显的原因(封闭源代码,所以我对此无能为力)。因此,我希望能够在给定的时间内启动这个进程,如果它在给定的时间后没有成功返回,则将其杀死。
有没有一个简单而健壮的方法可以用bash来实现?

eyh26e7m

eyh26e7m1#

您可以使用timeout *:

timeout 10 ping www.goooooogle.com

否则,执行timeout在内部执行的操作:

( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec ping www.goooooogle.com )

如果你想为更长的bash代码做一个超时,可以使用第二个选项:

( cmdpid=$BASHPID; 
    (sleep 10; kill $cmdpid) \
   & while ! ping -w 1 www.goooooogle.com 
     do 
         echo crap; 
     done )
  • 它包含在GNU Coreutils 8+中,因此大多数当前的Linux系统已经安装了它,否则您可以安装它,例如sudo apt-get install timeoutsudo apt-get install coreutils
qni6mghb

qni6mghb2#

# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) &

或者获取退出代码:

# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) & waiter=$!
# wait on our worker process and return the exitcode
exitcode=$(wait $pid && echo $?)
# kill the waiter subshell, if it still runs
kill -9 $waiter 2>/dev/null
# 0 if we killed the waiter, cause that means the process finished before the waiter
finished_gracefully=$?
1l5u6lss

1l5u6lss3#

sleep 999&
t=$!
sleep 10
kill $t
2o7dmzc5

2o7dmzc54#

我也有这个问题,发现另外两件事非常有用:

  1. bash中的SECONDS变量。
    1.命令“pgrep”。
    所以我在命令行中使用了类似这样的东西(OSX 10.9):
ping www.goooooogle.com & PING_PID=$(pgrep 'ping'); SECONDS=0; while pgrep -q 'ping'; do sleep 0.2; if [ $SECONDS = 10 ]; then kill $PING_PID; fi; done

由于这是一个循环,我包含了一个“sleep 0.2”来保持CPU的凉爽。- )
(BTW:ping是一个不好的例子,你只需要使用内置的“-t”(超时)选项。)

ia2d9nvy

ia2d9nvy5#

假设你有一个(或者可以很容易地创建一个)pid文件来跟踪子进程的pid,你可以创建一个脚本来检查pid文件的modtime,并根据需要杀死/重新启动进程。然后把脚本放在crontab中,让它在你需要的时间段运行。
如果你需要更多的细节,请告诉我。如果这听起来不适合你的需要,那么upstart?怎么样

cbwuti44

cbwuti446#

一种方法是在一个子shell中运行程序,并通过一个命名管道使用read命令与子shell通信。这样,您可以检查正在运行的进程的退出状态,并通过管道将此返回。
下面是一个yes命令在3秒后超时的例子。(可能只适用于Linux)。使用管道也有一些问题,因为打开管道进行读取的进程将挂起,直到它也被打开进行写入,反之亦然。因此,为了防止read命令挂起,我已经用一个后台子shell“楔入”打开了读管道(另一种防止冻结打开读-写管道的方法,即read -t 5 <>finished.pipe-然而,除了Linux之外,这也可能不起作用)。

rm -f finished.pipe
mkfifo finished.pipe

{ yes >/dev/null; echo finished >finished.pipe ; } &
SUBSHELL=$!

# Get command PID
while : ; do
    PID=$( pgrep -P $SUBSHELL yes )
    test "$PID" = "" || break
    sleep 1
done

# Open pipe for writing
{ exec 4>finished.pipe ; while : ; do sleep 1000; done } &  

read -t 3 FINISHED <finished.pipe

if [ "$FINISHED" = finished ] ; then
  echo 'Subprocess finished'
else
  echo 'Subprocess timed out'
  kill $PID
fi

rm finished.pipe
kkbh8khc

kkbh8khc7#

这里有一个尝试,试图避免在进程已经退出后杀死它,这减少了杀死具有相同进程ID的另一个进程的机会(尽管可能不可能完全避免这种错误)。

run_with_timeout ()
{
  t=$1
  shift

  echo "running \"$*\" with timeout $t"

  (
  # first, run process in background
  (exec sh -c "$*") &
  pid=$!
  echo $pid

  # the timeout shell
  (sleep $t ; echo timeout) &
  waiter=$!
  echo $waiter

  # finally, allow process to end naturally
  wait $pid
  echo $?
  ) \
  | (read pid
     read waiter

     if test $waiter != timeout ; then
       read status
     else
       status=timeout
     fi

     # if we timed out, kill the process
     if test $status = timeout ; then
       kill $pid
       exit 99
     else
       # if the program exited normally, kill the waiting shell
       kill $waiter
       exit $status
     fi
  )
}

使用run_with_timeout 3 sleep 10000,它运行sleep 10000,但在3秒后结束。
这就像其他答案使用后台超时进程在延迟后杀死子进程一样。我认为这与Dan的扩展答案(https://stackoverflow.com/a/5161274/1351983)几乎相同,除了如果超时shell已经结束,则不会被杀死。
在这个程序结束后,仍然会有一些延迟的“睡眠”进程在运行,但它们应该是无害的。
这可能是一个比我的其他答案更好的解决方案,因为它没有使用非便携式shell特性read -t,也没有使用pgrep

lsmd5eda

lsmd5eda8#

这里是我提交的第三个答案。这个答案处理信号中断,并在收到SIGINT时清理后台进程。它使用top answer中使用的$BASHPIDexec技巧来获取进程的PID(在本例中,sh调用中的$$)。它使用FIFO与负责kill和cleanup的子shell通信。(这就像我的second answer中的管道,但是有一个命名管道意味着信号处理程序也可以写入它。

run_with_timeout ()
{
  t=$1 ; shift

  trap cleanup 2

  F=$$.fifo ; rm -f $F ; mkfifo $F

  # first, run main process in background
  "$@" & pid=$!

  # sleeper process to time out
  ( sh -c "echo \$\$ >$F ; exec sleep $t" ; echo timeout >$F ) &
  read sleeper <$F

  # control shell. read from fifo.
  # final input is "finished".  after that
  # we clean up.  we can get a timeout or a
  # signal first.
  ( exec 0<$F
    while : ; do
      read input
      case $input in
        finished)
          test $sleeper != 0 && kill $sleeper
          rm -f $F
          exit 0
          ;;
        timeout)
          test $pid != 0 && kill $pid
          sleeper=0
          ;;
        signal)
          test $pid != 0 && kill $pid
          ;;
      esac
    done
  ) &

  # wait for process to end
  wait $pid
  status=$?
  echo finished >$F
  return $status
}

cleanup ()
{
  echo signal >$$.fifo
}

我已经尽可能地避免了竞争条件。然而,我无法消除的一个错误来源是当进程结束的时间与超时时间接近时。例如,run_with_timeout 2 sleep 2run_with_timeout 0 sleep 0。对我来说,后者给出了一个错误:

timeout.sh: line 250: kill: (23248) - No such process

因为它试图杀死已经自己退出的进程。

0tdrvxhp

0tdrvxhp9#

#Kill command after 10 seconds
timeout 10 command

#If you don't have timeout installed, this is almost the same:
sh -c '(sleep 10; kill "$$") & command'

#The same as above, with muted duplicate messages:
sh -c '(sleep 10; kill "$$" 2>/dev/null) & command'

相关问题