优雅的解决方案,以实现对于bash命令和函数超时 [英] Elegant solution to implement timeout for bash commands and functions

查看:97
本文介绍了优雅的解决方案,以实现对于bash命令和函数超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个函数来运行命令,其中有两个参数在1秒钟内指令第二超时:

 #! /斌/庆典功能RUN_CMD {
    CMD =$ 1;超时=$ 2
    grep的-Qp^ \\ D $ +<<< $超时||超时= 10    stderrfile = $(的readlink的/ proc / $$ / FD / 2)
    EXEC 2';&放大器; -     exitfile =的/ tmp /退出_ $(日期+%S%N)
    (EVAL$ CMD;回声$> $ exitfile)及    启动= $(日期+%S)
    而真实的;做
        PID = $(工种-l | awk的'/运行/ {打印$ 2})
        如果[-n$ PID];然后
            现在= $(日期+%S)
            运行= $(($现在 - $开始))
            如果[$运行-ge$超时];然后
                杀-15$ PID
                退出= 1
            科幻
            睡眠1
        其他
            打破
        科幻    DONE
    测试-n$退出||退出= $(猫$ exitfile)
    RM $ exitfile
    EXEC 2 - ; $ stderrfile
    回归$退出
}
功能sleep5 {
    睡眠5
    回声我睡5
    返回2
}RUN_CMD sleep56
RUN_CMD sleep53
回声嗨>和2

该功能工作正常,但我不知道它是一个完美的解决方案,我想知道关于替代以下


  1. 我有存储上的文件退出状态:(?的eval$ CMD;回声$> $ exitfile)

  2. 我关闭并重新打开STDERR: EXEC 2';&安培; - 和exec 2 - ; $ stderrfile

我将结束STDERR因为杀死命令时,我无法避免的消息:

test.sh:3号线:32323终止(?的eval$ CMD;回声$> $ exitfile)

PS:我知道暂停期望,但他们不会为职能的工作。

解决方案

这也许适合您的需要。我改变了调用签名,以使其能够避免使用评估

 #用法:run_with_timeoutñCMD ARGS ...
#或:run_with_timeout CMD ARGS ...
#在第二种情况下,CMD不能是数目和超时将为10秒。
run_with_timeout(){
    本地时间= 10
    如果[[$ 1 =〜^ [0-9] + $]];然后时间= $ 1;转移;科幻
    在子shell来避免作业控制信息#执行
    ($ @&放大器;
      孩子= $!
      在非交互的shell为SIGTERM#避免违约通知
      陷阱 - SIGTERM
      (睡眠时间$
        杀死孩子$ 2 - ;的/ dev / null的)及
      等待$孩子
    )
}

例子,显示退出状态:

  $ sleep_and_exit(){$睡眠{1:-1};退出$ {2:-0}; }$时间run_with_timeout 1 sleep_and_exit 3 0;回声$?真正0m1.007s
用户0m0.003s
SYS 0m0.006s
143$时间run_with_timeout 3 sleep_and_exit 1 0;回声$?真正0m1.007s
用户0m0.003s
SYS 0m0.008s
0$时间run_with_timeout 3 sleep_and_exit 1 7;回声$?真正0m1.006s
用户0m0.001s
SYS 0m0.006s
7

如图所示, run_with_timeout 的退出状态将是执行命令的退出状态,除非它是由超时,在这种情况下,这将是143(128丧生+ 15)。

请注意:如果您设置一个较大的超时和/或具有forkbomb运行,则可能回收的PID速度不够快杀儿杀死了错误的过程

I wrote a function to run commands, which takes two args 1st a command 2nd timeout in seconds:

#! /bin/bash

function run_cmd {
    cmd="$1"; timeout="$2"
    grep -qP "^\d+$" <<< "$timeout" || timeout=10

    stderrfile=$(readlink /proc/$$/fd/2)
    exec 2<&-

    exitfile=/tmp/exit_$(date +%s.%N)
    (eval "$cmd";echo $? > $exitfile) &

    start=$(date +%s)
    while true; do
        pid=$(jobs -l | awk '/Running/{print $2}')
        if [ -n "$pid" ]; then
            now=$(date +%s)
            running=$(($now - $start))
            if [ "$running" -ge "$timeout" ];then
                kill -15 "$pid"
                exit=1
            fi
            sleep 1
        else
            break
        fi

    done 
    test -n "$exit" || exit=$(cat $exitfile)
    rm $exitfile
    exec 2>$stderrfile              
    return "$exit"
}


function sleep5 {
    sleep 5
    echo "I slept 5"
    return 2
}

run_cmd sleep5 "6" 
run_cmd sleep5 "3"
echo "hi" >&2 

The function works fine but I am not sure it's an elegant solution, I would like to know about alternatives for the following

  1. I am having to store exit status on a file: (eval "$cmd";echo $? > $exitfile)
  2. I am closing and reopening STDERR: exec 2<&- and exec 2>$stderrfile

I am closing STDERR because I couldn't avoid the message when killing the command:

test.sh: line 3: 32323 Terminated ( eval "$cmd"; echo $? > $exitfile )

PS: I am aware of timeout and expect but they wouldn't work for functions.

解决方案

Perhaps this suits your needs. I changed the call signature to make it possible to avoid using eval.

# Usage: run_with_timeout N cmd args...
#    or: run_with_timeout cmd args...
# In the second case, cmd cannot be a number and the timeout will be 10 seconds.
run_with_timeout () { 
    local time=10
    if [[ $1 =~ ^[0-9]+$ ]]; then time=$1; shift; fi
    # Run in a subshell to avoid job control messages
    ( "$@" &
      child=$!
      # Avoid default notification in non-interactive shell for SIGTERM
      trap -- "" SIGTERM
      ( sleep $time
        kill $child 2> /dev/null ) &
      wait $child
    )
}

Example, showing exit status:

$ sleep_and_exit() { sleep ${1:-1}; exit ${2:-0}; }

$ time run_with_timeout 1 sleep_and_exit 3 0; echo $?

real    0m1.007s
user    0m0.003s
sys     0m0.006s
143

$ time run_with_timeout 3 sleep_and_exit 1 0; echo $?

real    0m1.007s
user    0m0.003s
sys     0m0.008s
0

$ time run_with_timeout 3 sleep_and_exit 1 7; echo $?

real    0m1.006s
user    0m0.001s
sys     0m0.006s
7

As shown, the exit status of run_with_timeout will be the exit status of the executed command unless it was killed by the timeout, in which case it will be 143 (128 + 15).

Note: If you set a large timeout and/or have a forkbomb running, you might recycle pids fast enough that the kill-child kills the wrong process.

这篇关于优雅的解决方案,以实现对于bash命令和函数超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆