2013-11-19 6 views
3

Я пытаюсь запустить программу в бесконечном цикле, потому что она иногда умирает без причины. Я хотел бы иметь возможность ударить Ctrl-C, чтобы предотвратить перезапуск программы.Как захватить Ctrl-C и использовать его, чтобы закончить бесконечный цикл

Я не хочу, чтобы Ctrl-C убивал программу, просто чтобы подождать, пока она не умрет, а затем не перезапустите ее снова.

theprogram - это винная программа (utorrent).

Бонусные баллы за то, что рассказали мне, как сделать так, чтобы он безопасно выходил из theprogram, точно так же, как нажимать на «х» в правом верхнем углу. Когда я вручную kill его из командной строки или нажав Ctrl-C, он не сможет запустить свой код очистки. Поэтому моя попытка просто остановить его перезапуска.

Я проверил несколько других вопросов об улавливании SIGINT, но я не мог понять, как это сделать.

Может ли кто-нибудь исправить этот код? Мой код, кажется, убивает theprogram, затем выходим из цикла, когда нажата комбинация Ctrl-C, не позволяя theprogram очистить.

#!/bin/bash 

EXIT=0 
trap exiting SIGINT 

exiting() { echo "Ctrl-C trapped, will not restart utorrent" ; EXIT=1;} 

while [ $EXIT -eq 0 ] ; do 
     wine theprogram 
     echo "theprogram killed or finished" 
     date 
     echo "exit code $?" 
     echo "sleeping for 20 seconds, then restarting theprogram..." 
     sleep 20 
done 

echo "out of loop" 
+0

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

+0

, и вам может быть сложно отказаться от использования «ctrl-C» для копирования (/ Paste) в этой винной программе? ... И знать, делали вы это или нет. кажется странным способом обработки перезапуска. Вы можете настроить ловушку для одного, если сигналы SIGUSR и «kill -SIG .... pid» (или у вас есть кнопка/скрипт, который делает это для вас) вместо –

+1

http://www.cons.org/cracauer/ sigint.html показывает, как работает SIGINT (и различные способы взаимодействия оболочек в зависимости от их режимов) –

ответ

1

Попробуйте это:

while true 
do 
    xterm -e wine theprogram || break 
    sleep 3 
done 

Трюк делается с помощью другого Xterm, чтобы начать вино. Таким образом, у вина есть другое управляющее tty и не будет затронуто прессом Ctrl-c.

Недостатком является то, что на вашем рабочем столе будет еще один xterm. Вы можете использовать опцию -iconic, чтобы начать ее отображение.

+0

приятно! и просто –

+0

@Alfe Я пробовал это, может быть, я что-то пропустил, но с этим кодом, нажав Ctrl-C в первом окне xterm, мгновенно закрывается другой xterm и utorrent, попав во второй xterm, мгновенно убивает utorrent, а второй xterm, чтобы снова перезапустить. Я просто использовал этот код без команды 'trap', так же, как вы его написали, но с' #!/Bin/bash' в начале. Не могли бы вы отредактировать свой ответ, чтобы поставить полный сценарий bash, который, по вашему мнению, я должен запустить там или объяснить, что я делаю неправильно? – localhost

+0

Что произойдет, если вы попробуете его с помощью 'xterm -e xterm || вместо этого? Второй xterm переживает Ctrl-c? У меня нет вашей торрент-программы для тестирования этого. Может быть, что материал на основе вина замечает закрытие процесса отца, а затем закрывается. В этом случае вы можете пойти с помощью сложного «xterm -e xterm -e wine theprogram || break'. – Alfe

1

Ну, я в конечном итоге не используя Ctrl-C, как в мой вопрос, потому что я не мог найти хорошее решение, но я использовал zenity чтобы всплывающее окно, в котором можно нажать, чтобы выйти из цикла:

zenity popup

#!/bin/bash 
zenity --info --title "thewineprogram" --text "Hit OK to disable thewineprogram auto-restart" & # run zenity in the background 
zen_pid=$! 

while : 
do 
    wine <wineprogramlocation> 
    EXITCODE=$? 
    echo "thewineprog killed or finished" 
    echo "exit code was $EXITCODE" 
    date 

    kill -0 $zen_pid > /dev/null 2>&1 # kill -0 just checks if a pid exists 
    if [ $? -eq 1 ] # process does not exist 
    then 
     break 
    fi 

    echo "sleeping for 5 seconds, then restarting the wine program..." 
    sleep 5  
done 

echo "finished" 
0

Похоже, что ловушка на SIGINT должна завершать выполняемую в данный момент подкоманду. Единственным исключением является обработчик с пустой строкой.

Чтобы продемонстрировать это: при нажатии ctrl-c это (trap "" INT;echo 1;sleep 5;echo 2) не останавливает команду сна. Однако это (trap "echo hi" INT;echo 1;sleep 5;echo 2) делает. После выполнения этого обработчика ловушки выполнение продолжается по следующей команде, в частности «echo 2». Таким образом, пустая строка как обработчик кажется особым случаем, который не убивает текущую подкоманду. Кажется, что нет никакого способа запустить обработчик, а не убить текущую подкоманду.

Почему это происходит: Shell forks + execs для выполнения каждой программы. В системном вызове exec он сбрасывает обработчики сигналов по умолчанию (процесс вызова перезаписывается, поэтому обработчики не работают). Игнорируемые сигналы унаследованы (см. «Man 2 execve», «man 7 signal» и POSIX.1; http://www.perlmonks.org/?node_id=1198044)

У меня была вторая идея: используйте «trap» «INT», чтобы полностью отключить ctrl-c, а затем отловить ctrl-z как сигнал для изящного выхода из вашей программы. Только захват ctrl-z (STP), похоже, не работает должным образом для меня. Когда я запускаю «ловушку» эхо-теста «TSTP; sleep 5») и нажимаю ctrl-z, моя оболочка висит. сон никогда не заканчивается через 5 секунд, и странно ctrl-c больше не работает. Я не знаю никаких других горячих клавиш-сигналов для использования, кроме ctrl-c и ctrl-z.Это известное поведение: см. Bash script: can not properly handle SIGTSTP.

0

Используйте процесс мониторинга:

Это позволяет сигнал SIGINT, чтобы ударить обработчик прерываний процесса монитор, не влияя на ребенка.

(это также может быть сделано в Perl, Python или любом языке)

#!/bin/bash 

cmd() { 
    trap '' INT 
    trap 'echo "Signal USR1 received (pid=$BASHPID)"; EXIT=1' USR1 
    EXIT=0 
    while [ $EXIT -eq 0 ] 
    do 
     echo "Starting (pid=$BASHPID)..." 
     sleep 5 # represents "wine theprogram" 
     echo "theprogram killed or finished" 
     date 
     echo "Exit code $?" 
     if [ $EXIT -eq 0 ]; then 
      echo "Sleeping for 2 seconds, then restarting theprogram..." 
      sleep 2 
     fi 
    done 
    echo "Exiting (pid=$BASHPID)" 
} 

run() { cmd & PID=$!; echo Started $PID; } 
graceful_exit() { kill -s USR1 $PID && echo "$PID signalled to exit (USR1)"; } 
shutdown() { kill -0 $PID 2>/dev/null && echo "Unexpected exit, killing $PID" && kill $PID; } 

trap 'graceful_exit' INT 
trap 'shutdown' EXIT 
run 

while : 
do 
    wait && break 
done 

echo "Exiting monitor process"