我想使用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,通过另一个ssh连接杀死remotescript
所以我不想知道如何获得remotepid? 我尝试了一些类似的东西:
remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!')
这不起作用,因为它会阻止。
如何等待变量打印然后“释放”子流程?
答案 0 :(得分:17)
最好保持清理由启动进程的ssh管理,而不是稍后使用第二个ssh会话进入kill。
当ssh连接到您的终端时;它表现得很好。但是,将它从终端中分离出来(正如您所注意到的那样)会发出信号或管理远程进程的痛苦。您可以关闭链接,但不能关闭远程进程。
这为您提供了一个选项:使用链接作为远程进程获取其需要关闭的通知的方式。最简单的方法是使用阻塞I / O.从ssh进行远程读取输入以及何时需要关闭进程;发送一些数据,以便遥控器的读取操作解锁,并且可以继续进行清理:
command & read; kill $!
这就是我们想要在遥控器上运行的内容。我们调用我们想要远程运行的命令;我们读了一行文本(块直到我们收到一个),当我们完成后,发出命令终止信号。
要将信号从我们的本地脚本发送到遥控器,我们现在需要做的就是发送一行文字。不幸的是,Bash在这里没有给你很多好的选择。至少,如果你想与bash兼容,那就不是了。 4.0。
使用bash 4,我们可以使用协同进程:
coproc ssh user@host 'command & read; kill $!'
trap 'echo >&"${COPROC[1]}"' EXIT
...
现在,当本地脚本退出时(不要陷入INT
,TERM
等等,只是EXIT
)它会在第二个元素中向该文件发送一个新行。 COPROC
数组。该文件是一个连接到ssh
的{{1}}的管道,有效地将我们的行路由到stdin
。 remote命令读取该行,结束命令的ssh
和read
。
在bash之前,由于我们没有共同进程,所以事情变得有点困难。在这种情况下,我们需要自己做管道:
kill
这应该适用于任何bash版本。
答案 1 :(得分:6)
试试这个:
ssh -tt host command </dev/null &
当您终止本地ssh进程时,远程pty将关闭,SIGHUP将被发送到远程进程。
答案 2 :(得分:1)
引用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。
更新: 辅助作业控制通道优于从标准输入读取。
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阻塞,直到服务器端进程完成,并且不会留下像&lt;这样的延迟进程。 (睡眠无限)可能。
ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1
实际上似乎没有将stdout重定向到任何地方,但它确实起到阻塞输入的作用,并避免捕获击键。
答案 3 :(得分:0)
bash 3.2的解决方案:
mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT
不起作用。 ssh命令不在“客户端”机器上的ps列表中。只有在我将某些内容回传到管道后,它才会显示在客户端计算机的进程列表中。 “服务器”机器上显示的进程只是命令本身,而不是读取/终止部分。
再次写入管道不会终止该过程。
总结一下,我需要写入管道以便启动命令,如果我再次写入,它不会像预期的那样终止远程命令。
答案 4 :(得分:-1)
您可能需要考虑安装远程文件系统并从主框运行脚本。例如,如果您的内核是使用fuse编译的(可以查看以下内容):
/sbin/lsmod | grep -i fuse
然后,您可以使用以下命令安装远程文件系统:
sshfs user@remote_system: mount_point
现在只需在mount_point中的文件上运行脚本。