Запуск процесса через 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
и kill
s команда.
перед 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.