LINUX.ORG.RU
ФорумAdmin

Как предотвратить зависание bash-скрипта?

 , ,


2

4

Всем привет!

Подскажите, есть ли способ предотвратить зависание bash-скрипта?

Например:

Скрипт вида:

command#1
command#2
command#3
....

command#1 выполняется, command#2 - зависает на выполнении.

Есть ли способ отловить зависание command#2 и перейти к выполнению command#3 или, хотя бы, корректно завершить скрипт?

Спасибо.

Что такое «зависание»?

man timeout

AITap ★★★★★
()

Постановка задачи странная. Скроее всего исходная проблема должна решатся иначе. Но из любви к искусству на шелле можно сделать что-то такое.

#!/bin/sh

run_timeout()
{
  timeout=$1
  $2 &
  pid=$!
  while ps $pid >/dev/null && [ $timeout -ne 0 ]; do
    sleep 1
    timeout=$((timeout - 1))
  done
  kill $pid 2>/dev/null && echo "Process $pid killed because executed too long"
}

test()
{
  echo "Will sleep for $1 seconds"
  sleep $1
}

run_timeout 5 'echo start'
run_timeout 5 'test 1'
run_timeout 5 'test 2'
run_timeout 5 'test 4'
run_timeout 5 'test 8'
run_timeout 5 'echo end'

unterwulf
()

Команды последовательно должны выполняться или пофих? И что за команды?

another ★★★★★
()
Ответ на: комментарий от unterwulf
timeout=$((timeout - 1))

Можуно чуть проще

((timeout--))
или
let timeout--
А ещё я бы
run_timeout 5 "kill $pid"
ps $pid >/dev/null && kill -9 $pid
Да, и из-за рекурсии, я бы на всякий случай объявил pid как local. Ну и timeout тоже.

Deleted
()
Последнее исправление: fargred (всего исправлений: 2)
Ответ на: комментарий от unterwulf

Уже же несколько лет как команда timeout (бинарный файл) входит в coreutils, зачем такая любовь к исскуству?

mky ★★★★★
()
Ответ на: комментарий от Deleted

Обрати внимание на шабанг моего скрипта. Твои улучшения — башизмы, POSIX shell их не поймёт. Добивание посредством kill -9 поддерживаю, в своём примере опустил его для краткости.

unterwulf
()
Ответ на: комментарий от mky

Спасибо, не знал. Правда, в моих coreutils её ещё нет, но у меня и версия 2008-го года :) А любовь к искусству она такая — не для оптимальности, но для красоты и переносимости. В стандартную установку OpenBSD, например, coreutils не входят, их нужно ставить отдельным пакетом.

unterwulf
()
Ответ на: комментарий от unterwulf

Если речь идёт не о команде, а об участке кода, что при более-менее сложном программировании на BASH и бывает чаще всего, то никакой timeout не поможет.

DRVTiny ★★★★★
()
Ответ на: комментарий от unterwulf

для проверки жив ли процесс можно использовать сигнал 0, и код возврата. в случае с башем это будет внутренная команда баша, а значит не форк.
kill -0 $pid

насчёт запуска с таймаутом, в example-скриптах баша должен валяться пример timeout авторства Chet Ramey 15летней давности:
http://www.bashcookbook.com/bashinfo/source/bash-4.1/examples/scripts/timeout
синтаксис использования примерно такой же как nice или nohup .

bl ★★★
()
Ответ на: комментарий от unterwulf

Примерно что-то похожее я и хочу реализовать.

Допустим необходимо отловить длительное выполнение функции:

function long_ping() {
	ping -c $1 8.8.8.8
}

На месте long_ping() может быть любой другой фрагмент кода, который выполняется дольше запланированого времени, зацикливается и т.д.

Утилита timeout не отрабатывает с функциями самого скрипта:

timeout -s 9 5 long_ping 100
после выполнения
timeout: failed to run command `long_ping': No such file or directory

При использовании самописной run_timeout()

run_timeout 5 "long_ping 100"
run_timeout 5 "long_ping 3"
в примере з sleep() работает адкветно. Но с long_ping() не прекращает выполнение команды.

arttykh
() автор топика
Ответ на: комментарий от Deleted

Обрати внимание на bash в тегах.

bash в тегах на ЛОРе означает просто шелл, т.к. те, кто задают вопросы по шеллу, часто не понимают разницу между bash и sh, но скрипт, написанный на POSIX shell будет работать и в sh и в dash и в bash, а вот написанный с использованием башизмов — только в bash. Не вижу причин советовать худший вариант.

unterwulf
()
Ответ на: комментарий от unterwulf

А я считаю, что если ТС не поленился три раза упомянуть bash, то именно его он и имел ввиду.

Deleted
()
Ответ на: комментарий от arttykh

в примере з sleep() работает адкветно. Но с long_ping() не прекращает выполнение команды.

Дело в том, что по таймауту убивается subshell, а команда, запущенная в нём, продолжает исполняться.

Я вижу как минимум три решения.

  • Не порождать subshell
    run_timeout 5 "ping -c 100 8.8.8.8"
    
  • Использовать exec
    long_ping() {
      exec ping -c $1 8.8.8.8
    }
    
  • Заставить subshell прибираться за собой
    long_ping() {
      trap 'kill $(jobs -p) 2>/dev/null' EXIT
      ping -c $1 8.8.8.8 &
      wait
    }
    
unterwulf
()
Ответ на: комментарий от unterwulf

А как быть с функцией вида:

function test_make() {
  ~some_code
  ...
  make
  ...
  ~some code
}

Subshell порождать необходимо, так как хочу контролировать время выполнения функции, а не команды. Использование exec make также не сработает.

arttykh
() автор топика
Ответ на: комментарий от arttykh

В этом случае могу предложить только вынести функцию в отдельный скрипт, который будет заботиться о том, чтобы, умирая, добивать всех своих потомков, и вызывать его через setsid.

Пример скрипта:

#!/bin/sh

trap 'pkill -g 0' EXIT
ping -c 100 8.8.8.8

Пример вызова:

run_timeout 5 'setsid ./ping.sh'

unterwulf
()
Ответ на: комментарий от Deleted

Оказывается, нельзя. Так то timeout передаёт потомком все полученные им сигналы, поэтому я думал, что можно запустить что-то вроде:

timeout 15 timeout -s KILL 25 ANY-PROCESS

но, каждый timeout создаёт свою группу и сигнал отправляет этой группе, поэтому сигнал от первого timeout не дойдёт до второго.

mky ★★★★★
()
Ответ на: комментарий от unterwulf

Проще принести с собой баш.

anonymous
()
Ответ на: комментарий от unterwulf

Захотел илиткой быть — полезай в кузовок.

anonymous
()
Ответ на: комментарий от bigbit

Не всякий POSIX shell умеет XPG4+. /bin/sh в Солярке тому пример.

Ну, вот и набежали несчастные админы Солярки :) Последний солярный шелл, с которым мне приходилось иметь дело, не умел даже $() (вместо ``), а уж sed там и вовсе был божественный... Это я к чему — каждый сам определяет степень своей упоротости. Если в скрипте пытаться угодить совсем уж всем, будет очень не красиво (для примера смотреть скрипты autotools). Поэтому я свою упоротость ограничил стандартом, на который я дал ссылку, а то ведь можно и пробел начать в шабангах ставить ;)

ВНИМАНИЕ! Пользователям ОС Solaris и нашим гостям из прошлого века строку

timeout=$((timeout - 1))
следует читать как
timeout=`expr $timeout - 1`

unterwulf
()
Ответ на: комментарий от unterwulf

Ну, c99 уже и в солярном gcc есть, в отличие от дефолтного sh :D

vasily_pupkin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.