LINUX.ORG.RU

Гарантированная остановка скрипта по ^C

 


0

0

Как добиться?
Бывает запускаешь в скрипте eix-sync или ещё что-то, что использует rsync, и при нажатии CTRL+C оно не сразу прекращается.
Бывает даже после этого в фоне rsync ещё висит.
Как 100% завершать скрипт и все его дочерние процессы по CTRL+C?

Я так понимаю eix-sync использует собственный обработчик сигналов и trap из родительского скрипта на него не действует.
Как-то всё-таки это можно сделать?

★★★★★

Ответ на: комментарий от egorcod

так trap родительского процесса на то, как обрабатывается нажатие CTRL+C в том, что запускается в скрипте не влияет

teod0r ★★★★★
() автор топика

Как 100% завершать скрипт и все его дочерние процессы

Посмотреть их pid-ы и сделать «kill -9 ...» :) . Умрут если не ждут завершения оперции ввода-вывода.

Ctrl-C - это SIGINT. Он перехватывается.

Есть eще Ctrl-\ - это SIGQUIT. Он тоже перехватывается, но реже.

Может подправить скрипт eix-sync ?

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

Посмотреть их pid-ы и сделать «kill -9 ...»

как это проще сделать через скрипт?

Может подправить скрипт eix-sync ?

что мне каждую программу подправлять?

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

Посмотреть их pid-ы и сделать «kill -9 ...» :) . Умрут если не ждут завершения оперции ввода-вывода.

это ты про uninterruptible sleep?

PunkoIvan ★★★★
()

Делай свой обработчик сигналов в скрипте. Не дожидайся отклика от дочернего процесса, а отслеживай его завершение, либо прибивай.

Black_Shadow ★★★★★
()
Последнее исправление: Black_Shadow (всего исправлений: 1)
Ответ на: комментарий от PunkoIvan

как?

#!/bin/bash
set -m
trap "kill -9 \$(jobs -p) $$" 0 2 9 15
eix-sync
trap - 0

-- не работает

teod0r ★★★★★
() автор топика
Последнее исправление: teod0r (всего исправлений: 1)
Ответ на: комментарий от Black_Shadow

Да, можно попробовать убить дочерний, чтоб змоби не плодить. и вспомнил про $$:

тогда так:

kill $! || kill -9 $! || kill $$ || kill -9 $$ 

А на какой сигнал вешать - то уже дело десятое. Главное, что б не было обработки в дочернем (перехвата)

PunkoIvan ★★★★
()

Надо сначала определиться, кто за собой не прибирается: скрипт или eix-sync. Ибо если eix-synс вызывается не в фоне, то SIGINT после ^C в него таки летит и это уже его ответственность прибить своих потомков. Скрипт тут ничего не поделает, т.к. не может узнать их pid-ы. А самого eix-sync хоть оббомбись kill-ами, это не поможет, наоборот, только хуже сделает, т.к. SIGKILL не даст ему за собой прибраться, даже если он и пытается.

Если нельзя чинить условно плохой eix-sync, то даже не зная pid-ов процессы можно попробовать поубивать на основании принадлежности к одной группе процессов (минус перед pid в команде kill), но тогда нужно обеспечить эту группу.

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

#!/bin/sh

if test -z "$OWNSID"; then
        export OWNSID=1
        exec setsid $0 "$@"
fi

trap 'kill -- -$$' TERM INT QUIT EXIT

for i in `seq 10`; do
        sleep 60 &
done
unterwulf
()
Ответ на: комментарий от unterwulf

благодарю.
сейчас конструкция:

#!/bin/bash

[[ $OWNSID ]] ||{
  export OWNSID=1
  exec -- setsid -w -- "$@" "$@"
}

set -m

trap "kill -9 -- \$(jobs -p) -$$" 0 2 3 9 15

eix-sync

trap - 0 3

позволяет сразу после CTRL+C получать приглашение, что мне и было нужно.
осталась одна проблема — в фоне остаются процессы (rsync). может, и это можно победить?

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

Плохого насоветовал. setsid форкается и скрипт уходит в фон и не получит уже SIGINT от терминала. Добавление опции -w эту ситуацию не улучшает. С ней setsid просто будет ждать завершения потомка, но, получив SIGINT, просто завершится сам, не перепослав сигнал потомку.

Но, похоже, bash сам всегда создаёт новую группу процессов, так что setsid делать не обязательно. Дополнительно нужно попросить шелл не создавать новые группы для каждой команды (по умолчанию в неинтерактивном режиме или set +m в интерактивном). Тогда по идее kill -$$ должен попасть во всё, что запущено из скрипта и не сбежало в отдельную группу.

Но в данном конкретном случае это всё равно не поможет, т.к. eix-sync это скрипт [а шелл сбегает в отдельную группу]. Увы.

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

Как уже сказали, чтобы работало 100% нужна как-то выделить пулл всех pid, создаваемых скриптом(и так далее по рекурсии). К примеру сделать так, чтобы скрипт запускался от отдельного юзера и затем убивать все процессы по этому юзеру.

Лично я так бы и сделал, потому что это самый простой метод, который будет работать для 97% всех скриптов.

Или, если все процессы, создаваемые скриптом можно отследить, то можно просто собирать $! в отдельную строку $local_pids, а на ^C разворачивать строку и убивать все запущенные процессы. Можно ещё написать функцию-инициализатор(допустим lp_init), которая будет все pid запускаемых процессов собирать в $local_pids - и запускать процессы так: lp_init eix; и функцию-киллер, которая будет проверять какие pid ещё работают, и вырубать именно их - и повесить её на ^С.

Это всё элементарно и понятно.

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

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

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

Можно ещё написать функцию-инициализатор(допустим lp_init), которая будет все pid запускаемых процессов собирать в $local_pids - и запускать процессы так: lp_init eix;

а как сделать чтоб функция собирала пиды? через & и $! ?

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

так работает как надо.
жаль только нельзя объединить команды в {} чтобы можно было

lp_init {
echo
eix-sync
...; }

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

Или, если все процессы, создаваемые скриптом можно отследить, то можно просто собирать $! в отдельную строку $local_pids, а на ^C разворачивать строку и убивать все запущенные процессы.

Этот список шелл сам ведёт. pid-ы запущенных им в фоне процессов выводит команда jobs -p. Проблема узнать pid-ы потомков потомков и далее по иерархии.

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

Этот список шелл сам ведёт. pid-ы запущенных им в фоне процессов выводит команда jobs -p.

но приведённый вариант работает, а до этого с set -m и jobs -p не работало.

Проблема узнать pid-ы потомков потомков и далее по иерархии.

как-то парсить /proc/ ?

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

но приведённый вариант работает, а до этого с set -m и jobs -p не работало.

Если речь про Гарантированная остановка скрипта по ^C (комментарий) , то там не работало потому что eix-sync запускался не в фоне. Соответственно вывод jobs -p был пустой. А set -m там вообще мимо кассы ;)

как-то парсить /proc/ ?

На мой вкус для скрипта это выглядит как слишком много возни.

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

там не работало потому что eix-sync запускался не в фоне

во магия. не знал

вывод jobs -p был пустой

дело оказалось не в jobs -p, а в минусе перед $$

А set -m там вообще мимо кассы

без него не работал бы jobs -p

teod0r ★★★★★
() автор топика
Последнее исправление: teod0r (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.