Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用bash通过ssh启动进程,然后在sigint上终止它_Bash_Ssh_Signals - Fatal编程技术网

使用bash通过ssh启动进程,然后在sigint上终止它

使用bash通过ssh启动进程,然后在sigint上终止它,bash,ssh,signals,Bash,Ssh,Signals,我想使用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进程仍在运行远程计算机。如果在终端窗口

我想使用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 $!')
这不起作用,因为它会阻塞


< >如何等待变量打印,然后“释放”子进程?< /p> ,您可能需要考虑安装远程文件系统并从主框中运行脚本。例如,如果您的内核是使用fuse编译的(可以检查以下内容):

然后,可以使用以下命令装载远程文件系统:

sshfs user@remote_system: mount_point

现在,只需在mount_point中的文件上运行脚本。

最好由启动进程的ssh管理清理,而不是在稍后使用第二个ssh会话进行清除

当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
stdin
的管道,有效地将我们的线路路由到
ssh
。远程命令读取行,结束
read
kill
s命令

在Bash4之前,事情变得有点困难,因为我们没有协同进程。在这种情况下,我们需要自己制作管道:

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT
mkfifo/tmp/myshcommand
sshuser@host命令与阅读;杀死$!'/tmp/myshcommand;rm/tmp/myshCommand“退出”
这几乎适用于任何bash版本。

尝试以下方法:

ssh -tt host command </dev/null &

ssh-tt主机命令bash 3.2的解决方案:

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT
mkfifo/tmp/myshcommand
sshuser@host命令与阅读;杀死$!'/tmp/myshcommand;rm/tmp/myshCommand“退出”
不起作用。ssh命令不在“客户端”计算机上的ps列表中。只有在我将某个内容回显到管道中之后,它才会出现在客户机的进程列表中。出现在“服务器”机器上的进程只是命令本身,而不是读取/终止部分

再次写入管道不会终止进程


总之,我需要向管道中写入命令才能启动,如果我再次写入,它不会像预期的那样终止远程命令。

引用了lhunah的答案,我想出了这个脚本

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 "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
设置作业控制模式并监控作业控制通道:

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上一个bug的引用:


这是我找到的最好的方法。您需要在服务器端尝试读取stdin,然后在失败时终止进程组,但您也需要在客户端使用stdin,在服务器端进程完成之前阻塞,并且不会留下延迟进程,就像您在远程脚本中尝试过陷阱一样?也许是叹息?我试着捕捉我能想到的所有信号,但没有成功。。。我想我只需要再次ssh到远程机器并在脚本上运行kill,但是我需要远程进程的pid…如果你要这样做,请尝试
pkill
。粗糙但通常有效。问题是我可能有多个脚本实例在运行。我只想杀死远程实例,我100%确定它们是从我正在杀死的区域设置实例派生出来的真正脚本的目的是将工作分发到几个从机上,所以这对我不起作用。这应该起作用。脚本需要在Bash3.2上运行,所以我必须自己制作管道。正常情况下,远程进程正常结束,因此我不能依靠陷阱来清理fifo。(如果我把它们都列在一个列表中,并在程序结束时进行清理,这是可能的,但我宁愿在处理完它们后尽快将它们删除)。这种方法是可行的,但如果程序正常结束,问题现在就来了:读取调用仍然需要输入才能结束。你知道如何处理这两种情况吗?@jkp:EXIT也会在程序正常结束时触发。它应该涵盖这两种情况。这对于脚本编写并不理想,尤其是在将脚本管道化到ss时
 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"
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
ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1