Запуск процесса над 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.