Запуск процесса через ssh с использованием bash, а затем его уничтожение на sigint - PullRequest
11 голосов
/ 13 июля 2010

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

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

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

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

Однако панельПроцесс .sh по-прежнему работает на удаленном компьютере.Если я выполняю те же команды в окне терминала, это останавливает процесс на удаленном хосте.

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

edit: Похоже, мне нужно использовать опцию B, убивая удаленный скрипт через другое соединение ssh

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

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

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

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

Ответы [ 5 ]

17 голосов
/ 05 августа 2010

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

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

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

command & read; kill $!

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

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

В bash 4 мы можем использовать совместные процессы:

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

Теперь, когда локальный скрипт завершает работу (не перехватывает INT, TERM и т. Д. Просто EXIT), он отправляет новую строку в файл во втором элементе массива COPROC. Этот файл представляет собой канал, который подключен к ssh 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.

6 голосов
/ 13 ноября 2011

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

ssh -tt host command </dev/null &

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

1 голос
/ 17 сентября 2014

Ссылка на ответ по 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 может быть предпочтительнее, чем kill PID, в зависимости от поведения, необходимого для порожденных дочерних процессов.Вы также можете убить -HUP или kill -INT, если хотите.

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

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

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

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

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

0 голосов
/ 08 мая 2013

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

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

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

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

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

0 голосов
/ 16 июля 2010

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

/sbin/lsmod | grep -i fuse

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

sshfs user@remote_system: mount_point

Nowпросто запустите ваш скрипт для файла, расположенного в mount_point.

...