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

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

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

#!/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 сценарий? Или мне нужно заставить его войти в систему на удаленной машине, найти правильный процесс и убить его таким образом?

изменить: Кажется, я должен пойти с опцией B, убивая remotescript через другое ssh-соединение

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

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

это не работает, так как он блокирует.

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

5 ответов


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

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

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

command & read; kill $!

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

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

С bash 4 мы можем использовать co-процессы:

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

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

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

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

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


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

ssh -tt host command </dev/null &

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


ссылаясь на ответ lhunath и https://unix.stackexchange.com/questions/71205/background-process-pipe-input я придумал этот сценарий

run.sh:

#/bin/bash
log="log"                                                                                 
eval "$@" \&                                                                              
PID=$!                                                                                    
echo "running" "$@" "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 или убить-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 $$) &
"$@" &
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 в любом месте, но он работает как блокирующий вход и избегает захвата нажатий клавиш.


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

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

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

повторная запись в канал не завершает процесс.

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


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

/sbin/lsmod | grep -i fuse

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

sshfs user@remote_system: mount_point

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