在脚本中使用`until`和`/ usr / bin / timeout`

问题描述 投票:6回答:5

我想在bash脚本中执行一个大约需要1分钟才能完成的命令。但是,有时这个命令会挂起,所以我想在循环中使用/ usr / bin / timeout直到它工作。

如果我使用timeout 300 mycommand myarg1,它可以工作,但如果我在下面的循环中使用它在bash中,它不会打印任何东西(甚至不是我的命令打印的典型输出)并且它会挂起!:

until timeout 300 mycommand myarg
do
    echo "The command timed out, trying again..."
done

我的bash版本:

$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

我的超时版本:

$ timeout --version
timeout (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

(标准Ubuntu16.04)

bash loops while-loop timeout
5个回答
2
投票

这个问题

(mycommand args) & pid=$!
sleep 1000 && kill -INT $pid

是它总是需要1000秒。采用更主动(和CPU消耗)的方法将缩短时间:

typeset -i i 
i=0
command with arguments &
pid=$!
while ps $pid > /dev/null 2>/dev/null ; do
    i=$i+1
    if [ $i -gt 999 ] ; then
        kill -9 $pid
    fi
    sleep 1
done

或者,如果您的系统不是太忙或间隔时间很短:

command with arguments &
pid=$! 
echo "kill -9 $pid" | at "now + 15 minutes"
wait

但是timeout当然也会奏效。

之所以

until timeout 300 mycommand myarg
do
    echo "The command timed out, trying again..."
done

挂起是bash将尝试评估你的until的条件,这可能需要300秒。如果timeout 300 mycommand myarg返回成功,则从不执行until。试试例子:

if timeout 30 mycommand myarg ; then
    echo "The timeout of mycommand succeeded"
else
    echo "It failed! What a pitty"
fi 

2
投票

这就像一个魅力。

命令完成时:

sh timeoutLoop.sh 4

OUTPUT:

Seconds start now...
3 seconds passed...

命令未完成时:

sh timeoutLoop.sh 2

OUTPUT:

Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
...

你的mycommand = waitSeconds.sh

#!/bin/bash -

echo "Seconds start now..."
sleep $1
echo "$1 seconds passed..."

并循环bash timeoutLoop.sh

#!/bin/bash -

until timeout $1 waitSeconds.sh 3
do
echo 'The command timed out, trying again...'
done

试试这个,不要忘记脚本的标题。


1
投票

我无法准确解释发生了什么,但似乎当时间到期时,超时使用kill返回0(成功)所以在完成shell时看看$?并重新启动该过程。

你的回声“命令超时,再试一次......”正是发生了什么。

所以试试这种方式。

until timeout 20 find / -name "*.sh" 2>/dev/null ;do
  echo "The command timed out, not trying again..."
  break
done

1
投票

使用此脚本,您可以将所需的超时配置为变量。它在后台启动进程,获取进程ID,并每秒检查以查看进程是否仍在运行。如果是这样,则打印一个点,因此过程状态非常明显。它将报告后台线程的完成,如果它运行的时间超过预期,则终止它。此配置中的循环使用非常少的开销。

timeout=300
wait() {
    sleep 1 # sleep for one second
    echo -n . # show that we are still waiting
    [ `ps|grep $pid|wc -l` -gt 0 ] && { # background process still running?
        [ `ps -p $pid -o etimes|grep [0-9]` -gt $timeout ] && { # running too long?
            echo "Time expired.";
            kill $pid;
        } || {
            wait; # keep waiting
        }
    } || {
        echo "DONE!";
    }
}
sleep 20 & # replace this with mycommand &
pid=$!
wait

0
投票

我最近不得不找到一个杀死一个无法完成的过程的解决方案。这就是我设法提出的:

(mycommand args) & pid=$!
sleep 1000 && kill -INT $pid

应该是自我解释的。运行您的命令并获取进程ID。超时后杀死它。

我的具体用途是扫描DVB-S:

(w_scan -a 7 -E 0 -f s -c $1 -s $2 -o 7 -x >$tunefile) & pid=$!
sleep 1500 && kill -INT $pid

w_scan命令永远不会完成,并将永远循环扫描相同的频率。

编辑:您可以检查pid是否仍在运行:这至少会允许您的脚本采取相应的行动。

if ps -p $id > /dev/null
then 
    : active
else
    : inactive
fi

EDIT2:我刚刚使用ffmpeg运行了一个快速脚本,从一种格式转换为另一种格式。我的测试将mkv(2Gb文件)转换为mp4。这通常需要很长时间,但我想让它在10秒内工作然后退出。

它并不多,但最初的测试运行良好。

film=/home/wlgfx/longfilm.mkv
output=/home/wlgfx/longfilm.mp4

(ffmpeg -i $film -y -vcodec copy -acodec copy $output) & pid=$!
#for i in 'seq 1 10' # 10 seconds
#do
#    sleep 1
    if wait $pid; then echo "$pid success $?"
    else echo "$pid fail $?"
    fi
#done

退出状态是$?

我还注意到ffmpeg只花了大约5秒钟将2Gb文件转换成另一个容器。我将进行更新,以便它进行转码,然后我会对脚本进行进一步的更改以反映它们,以便在x秒后终止它。

目前,我在这里看:https://stackoverflow.com/a/1570351/2979092

编辑4:通过运行单独的超时过程,这次,如果您的进程在超时内正常退出,则将显示成功的退出代码。否则它将终止挂起过程。

film=/home/wlgfx/longfilm.mkv
output=/home/wlgfx/longfilm.mp4

while : ; do

    #(ffmpeg -i $film -y -vcodec mpeg2video -acodec copy $output) & pid=$!
    (ffmpeg -i $film -y -vcodec copy -acodec copy $output) & pid=$!

    (sleep 25 ; echo 'timeout'; kill $pid) & killpid=$!

    wait $pid # will get status if completed successfully
    status=$?

    (kill -0 $killpid && kill $killpid) || true

    [[ $status == 0 ]] || break

done
© www.soinside.com 2019 - 2024. All rights reserved.