Запуск процесса над ssh с помощью bash, а затем его уничтожение по команде sigint

Я хочу начать пару заданий на разных машинах с помощью ssh. Если пользователь прерывает основной script, я хочу закончить все задания.

Вот краткий пример того, что я пытаюсь сделать:

#!/bin/bash
trap "aborted" SIGINT SIGTERM
aborted() {
    kill -SIGTERM $bash2_pid
    exit
}

ssh -t remote_machine /foo/bar.sh &
bash2_pid=$!
wait

Однако процесс bar.sh все еще работает с удаленной машиной. Если я делаю те же команды в терминальном окне, он отключает процесс на удаленном хосте.

Есть ли простой способ сделать это, когда я запускаю bash script? Или мне нужно сделать это для входа в систему на удаленном компьютере, найти нужный процесс и убить его таким образом?

изменить: Похоже, мне нужно идти с вариантом B, убивая remotescript через другое ssh-соединение

Так что я не хочу знать, как мне получить удаленный? Я пробовал что-то вроде:

remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!')

Это не работает, поскольку он блокируется.

Как ждать, пока переменная будет напечатана, а затем "отпустите" подпроцесс?

Ответ 1

Было бы предпочтительнее сохранить вашу очистку, управляемую ssh, которая запускает процесс, а не перемещается для убийства со второй сессией ssh ​​позже.

Когда ssh подключен к вашему терминалу; он ведет себя довольно хорошо. Однако отсоедините его от вашего терминала, и он становится (как вы заметили) болью, чтобы сигнализировать или управлять удаленными процессами. Вы можете закрыть ссылку, но не удаленные процессы.

Это оставляет вам один вариант: используйте ссылку как способ для удаленного процесса получить уведомление о необходимости закрыть его. Самый чистый способ сделать это - использовать блокирующий ввод-вывод. Сделайте удаленный ввод для чтения из ssh и когда вы хотите, чтобы процесс был выключен; отправьте ему некоторые данные, чтобы удаленная операция чтения разблокировалась, и она может продолжить очистку:

command & read; kill $!

Это то, что мы хотели бы запустить на пульте дистанционного управления. Мы вызываем нашу команду, которую хотим запустить удаленно; мы читаем строку текста (блоки до тех пор, пока мы ее не получим), и когда мы закончим, сообщите о завершении команды.

Чтобы отправить сигнал с нашего локального script на удаленный, все, что нам нужно сделать, это отправить ему текст. К сожалению, Bash не дает вам много хороших вариантов. По крайней мере, если вы хотите быть совместимым с Bash < 4.0.

С Bash 4 мы можем использовать совлокальные процессы:

coproc ssh [email protected] 'command & read; kill $!'
trap 'echo >&"${COPROC[1]}"' EXIT
...

Теперь, когда локальный script завершает работу (не ловушка на INT, TERM и т.д. Просто EXIT), он отправляет новую строку в файл во втором элементе COPROC массив. Этот файл представляет собой канал, который подключен к ssh stdin, эффективно маршрутизируя нашу строку до ssh. Удаленная команда считывает строку, завершает команду read и kill.

До Bash 4 вещи становятся немного сложнее, так как у нас нет совлокальных процессов. В этом случае нам нужно сделать сам трубопровод:

mkfifo /tmp/mysshcommand
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

Это должно работать практически в любой версии Bash.

Ответ 2

Попробуйте следующее:

ssh -tt host command </dev/null &

Когда вы убиваете локальный процесс ssh, удаленный pty закроется, и SIGHUP будет отправлен на удаленный процесс.

Ответ 3

Ссылка на ответ по lhunath и https://unix.stackexchange.com/questions/71205/background-process-pipe-input Я придумал этот script

run.sh:

#/bin/bash
log="log"                                                                                 
eval "[email protected]" \&                                                                              
PID=$!                                                                                    
echo "running" "[email protected]" "in PID $PID"> $log                                                   
{ (cat <&3 3<&- >/dev/null; kill $PID; echo "killed" >> $log) & } 3<&0                              
trap "echo EXIT >> $log" EXIT                                                             
wait $PID

Разница в том, что эта версия убивает процесс, когда соединение закрыто, но также возвращает код выхода команды, когда она выполняется до завершения.

 $ ssh localhost ./run.sh true; echo $?; cat log
 0
 running true in PID 19247
 EXIT

 $ ssh localhost ./run.sh false; echo $?; cat log
 1
 running false in PID 19298
 EXIT

 $ ssh localhost ./run.sh sleep 99; echo $?; cat log
 ^C130
 running sleep 99 in PID 20499
 killed
 EXIT

 $ ssh localhost ./run.sh sleep 2; echo $?; cat log
 0
 running sleep 2 in PID 20556
 EXIT

Для однострочного:

 ssh localhost "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"

Для удобства:

 HUP_KILL="& PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
 ssh localhost "sleep 99 $HUP_KILL"

Примечание: kill 0 может быть предпочтительнее убить $PID в зависимости от поведения, необходимого для порожденных дочерних процессов. Вы также можете убить -HUP или kill -INT, если хотите.

Обновление: Дополнительный канал управления заданиями лучше, чем чтение из stdin.

ssh -n -R9002:localhost:8001 -L8001:localhost:9001 localhost ./test.sh sleep 2

Установите режим управления заданиями и контролируйте канал управления заданиями:

set -m
trap "kill %1 %2 %3" EXIT
(sleep infinity | netcat -l 127.0.0.1 9001) &
(netcat -d 127.0.0.1 9002; kill -INT $$) &
"[email protected]" &
wait %3

Наконец, вот еще один подход и ссылка на ошибку, поданную на openssh: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

Это лучший способ, который я нашел для этого. Вы хотите что-то на стороне сервера, которое пытается прочитать stdin, а затем убивает группу процессов, когда это не удается, но вы также хотите, чтобы stdin на стороне клиента блокировался до тех пор, пока процесс на стороне сервера не будет выполнен и не оставит затяжные процессы, такие как < (бесконечность сна).

ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1

На самом деле нет перенаправления stdout в любом месте, но он функционирует как блокирующий вход и позволяет избежать захвата нажатий клавиш.

Ответ 4

Решение для bash 3.2:

mkfifo /tmp/mysshcommand
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

не работает. Команда ssh не находится в списке ps на машине "клиент". Только после того, как я повторю что-то в трубе, он появится в списке процессов клиентской машины. Процесс, который появляется на "серверной" машине, будет просто самой командой, а не частью чтения/уничтожения.

Запись в трубку не завершает процесс.

Итак, подведем итог, мне нужно записать в трубу команду для запуска, и если я напишу еще раз, она не уничтожит удалённую команду, как ожидалось.

Ответ 5

Возможно, вам захочется установить удаленную файловую систему и запустить script из главного окна. Например, если ваше ядро ​​скомпилировано с помощью плавкого предохранителя (можно проверить со следующим):

/sbin/lsmod | grep -i fuse

Затем вы можете подключить удаленную файловую систему с помощью следующей команды:

sshfs [email protected]_system: mount_point

Теперь просто запустите ваш script в файле, расположенном в mount_point.