Как ограничить время работы BASH script
У меня есть длинный BASH script, который я запускаю под CYGWIN в Windows.
Я хотел бы ограничить работу script на 30 секунд и автоматически прекратить работу, если он превысит этот предел. В идеале, я хотел бы иметь возможность сделать это с любой командой.
Например:
sh-3.2$ limittime -t 30 'myscript.sh'
или
sh-3.2$ limittime -t 30 'grep func *.c'
В cygwin команда ulimit, похоже, не работает.
Я открыт для любых идей.
Ответы
Ответ 1
См. http://www.pixelbeat.org/scripts/timeout script, функциональность которого была интегрирована в более новые ядра:
#!/bin/sh
# Execute a command with a timeout
# License: LGPLv2
# Author:
# http://www.pixelbeat.org/
# Notes:
# Note there is a timeout command packaged with coreutils since v7.0
# If the timeout occurs the exit status is 124.
# There is an asynchronous (and buggy) equivalent of this
# script packaged with bash (under /usr/share/doc/ in my distro),
# which I only noticed after writing this.
# I noticed later again that there is a C equivalent of this packaged
# with satan by Wietse Venema, and copied to forensics by Dan Farmer.
# Changes:
# V1.0, Nov 3 2006, Initial release
# V1.1, Nov 20 2007, Brad Greenlee <[email protected]>
# Make more portable by using the 'CHLD'
# signal spec rather than 17.
# V1.3, Oct 29 2009, Ján Sáreník <[email protected]>
# Even though this runs under dash,ksh etc.
# it doesn't actually timeout. So enforce bash for now.
# Also change exit on timeout from 128 to 124
# to match coreutils.
# V2.0, Oct 30 2009, Ján Sáreník <[email protected]>
# Rewritten to cover compatibility with other
# Bourne shell implementations (pdksh, dash)
if [ "$#" -lt "2" ]; then
echo "Usage: `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi
cleanup()
{
trap - ALRM #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null && #kill last job
exit 124 #exit with 124 if it was running
}
watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}
watchit $1& a=$! #start the timeout
shift #first param was timeout for sleep
trap "cleanup" ALRM INT #cleanup after timeout
"[email protected]"& wait $!; RET=$? #start the job wait for it and save its return value
kill -ALRM $a #send ALRM signal to watchit
wait $a #wait for watchit to finish cleanup
exit $RET #return the value
Ответ 2
Следующий script показывает, как это сделать, используя фоновые задачи. Первый раздел убивает 60-секундный процесс после 10-секундного предела. Вторая попытка убить процесс, который уже вышел. Имейте в виду, что если вы установите очень высокий тайм-аут, идентификаторы процессов могут перевернуться, и вы убьете неправильный процесс, но это скорее теоретическая проблема - тайм-аут должен быть очень большим, и вам нужно будет начиная множество процессов.
#!/usr/bin/bash
sleep 60 &
pid=$!
sleep 10
kill -9 $pid
sleep 3 &
pid=$!
sleep 10
kill -9 $pid
Здесь вывод на моем ящике Cygwin:
$ ./limit10
./limit10: line 9: 4492 Killed sleep 60
./limit10: line 11: kill: (4560) - No such process
Если вы хотите только дождаться завершения процесса, вам нужно ввести цикл и проверить. Это немного менее точно, так как sleep 1
и другие команды на самом деле занимают более одной секунды (но не намного больше). Используйте этот script для замены второго раздела выше (команды "echo $proc
" и "date
" предназначены для отладки, я не ожидал бы их в конечном решении).
#!/usr/bin/bash
date
sleep 3 &
pid=$!
((lim = 10))
while [[ $lim -gt 0 ]] ; do
sleep 1
proc=$(ps -ef | awk -v pid=$pid '$2==pid{print}{}')
echo $proc
((lim = lim - 1))
if [[ -z "$proc" ]] ; then
((lim = -9))
fi
done
date
if [[ $lim -gt -9 ]] ; then
kill -9 $pid
fi
date
В основном это петли, проверяя, продолжает ли процесс каждую секунду. Если нет, он выходит из цикла со специальным значением, чтобы не пытаться убить его. В противном случае он истекает и убивает ребенка.
Здесь вывод для sleep 3
:
Mon Feb 9 11:10:37 WADT 2009
pax 4268 2476 con 11:10:37 /usr/bin/sleep
pax 4268 2476 con 11:10:37 /usr/bin/sleep
Mon Feb 9 11:10:41 WADT 2009
Mon Feb 9 11:10:41 WADT 2009
и a sleep 60
:
Mon Feb 9 11:11:51 WADT 2009
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
Mon Feb 9 11:12:03 WADT 2009
Mon Feb 9 11:12:03 WADT 2009
./limit10: line 20: 4176 Killed sleep 60
Ответ 3
Отметьте эту ссылку. Идея состоит в том, что вы запускаете myscript.sh
в качестве подпроцесса вашего script и записываете его PID, а затем убиваете его, если он работает слишком долго.
Ответ 4
Вы можете запустить команду в качестве фонового задания (т.е. с помощью "&" ), используйте переменную bash для "pid последнего запуска команды", сон в течение необходимого количества времени, затем запустите kill
с помощью что pid.