如何终止后台/分离的 ssh 会话?

发布于 2024-08-13 04:27:09 字数 971 浏览 2 评论 0原文

我正在将程序协同与 ssh 隧道一起使用

它可以工作,我只需打开一个控制台并键入这两个命令:

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

因为我很懒,我制作了一个 Bash 脚本,只需在图标上单击鼠标即可运行:

#!/bin/bash
ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

Bash 脚本上面的方法也可以,但现在我还想通过单击鼠标来杀死 synergy 和 ssh 隧道,所以我必须将 synergy 和 ssh 的 PID 保存到文件中以便稍后杀死它们:

#!/bin/bash

mkdir -p /tmp/synergyPIDs || exit 1
rm -f /tmp/synergyPIDs/ssh || exit 1
rm -f /tmp/synergyPIDs/synergy || exit 1

[ ! -e /tmp/synergyPIDs/ssh ] || exit 1
[ ! -e /tmp/synergyPIDs/synergy ] || exit 1

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
echo $! > /tmp/synergyPIDs/ssh
synergyc localhost
echo $! > /tmp/synergyPIDs/synergy

但是这个脚本的文件是空的。

如何获取 ssh 和 synergy 的 PID?
(我尽量避免使用 ps aux | grep ... | awk ... | sed ... 组合,必须有一种更简单的方法。)

I am using the program synergy together with an ssh tunnel

It works, i just have to open an console an type these two commands:

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

because im lazy i made an Bash-Script which is run with one mouseclick on an icon:

#!/bin/bash
ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

the Bash-Script above works as well, but now i also want to kill synergy and the ssh tunnel via one mouseclick, so i have to save the PIDs of synergy and ssh into file to kill them later:

#!/bin/bash

mkdir -p /tmp/synergyPIDs || exit 1
rm -f /tmp/synergyPIDs/ssh || exit 1
rm -f /tmp/synergyPIDs/synergy || exit 1

[ ! -e /tmp/synergyPIDs/ssh ] || exit 1
[ ! -e /tmp/synergyPIDs/synergy ] || exit 1

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
echo $! > /tmp/synergyPIDs/ssh
synergyc localhost
echo $! > /tmp/synergyPIDs/synergy

But the files of this script are empty.

How do I get the PIDs of ssh and synergy?
(I try to avoid ps aux | grep ... | awk ... | sed ... combinations, there has to be an easier way.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

我们的影子 2024-08-20 04:27:09

谨此向 pgreppkillps | 的用户致以应有的尊重。 awk 等,有一个得多更好的方法。

考虑一下,如果您依赖 ps -aux | grep ... 来查找您面临冲突风险的进程。您可能有一个不太可能发生的用例,但作为一般规则,这不是正确的方法。

SSH 提供了一种管理和控制后台进程的机制。但就像许多 SSH 事物一样,它是一个“高级”功能,许多人(从这里的其他答案看来)并不知道它的存在。

在我自己的用例中,我家里有一台工作站,我想在其中留下一条连接到办公室内部网络上的 HTTP 代理的隧道,还有另一个工作站可以让我快速访问位于同一位置的服务器上的管理界面。这就是您可以创建从家里启动的基本隧道的方式:

$ ssh -fNT -L8888:proxyhost:8888 -R22222:localhost:22 officefirewall
$ ssh -fNT -L4431:www1:443 -L4432:www2:443 colocatedserver

这会导致 ssh 进入后台,使隧道保持打开状态。但是如果隧道消失了,我就会被困住,如果我想找到它,我必须解析我的进程列表和主页,我已经得到了“正确的”ssh(以防我不小心启动了多个看起来像相似的)。

相反,如果我想管理多个连接,我可以使用 SSH 的 ControlMaster 配置选项以及 -O 命令行选项进行控制。例如,在我的 ~/.ssh/config 文件中,

host officefirewall colocatedserver
    ControlMaster auto
    ControlPath ~/.ssh/cm_sockets/%r@%h:%p

上面的 ssh 命令在运行时会在 ~/.ssh/cm_sockets/ 中留下 spoor,其中然后可以提供控制访问权限,例如:

$ ssh -O check officefirewall
Master running (pid=23980)
$ ssh -O exit officefirewall
Exit request sent.
$ ssh -O check officefirewall
Control socket connect(/home/ghoti/.ssh/cm_socket/[email protected]:22): No such file or directory

此时,隧道(以及控制 SSH 会话)消失了,无需使用锤子(killkillallpkill 等)。

将其带回您的用例...

您正在建立隧道,希望 syngergyc 通过该隧道与 TCP 端口上的 syngergys 进行通信12345. 为此,我会执行以下操作。

在您的 ~/.ssh/config 文件中添加一个条目:

Host otherHosttunnel
    HostName otherHost
    User otherUser
    LocalForward 12345 otherHost:12345
    RequestTTY no
    ExitOnForwardFailure yes
    ControlMaster auto
    ControlPath ~/.ssh/cm_sockets/%r@%h:%p

请注意,命令行 -L 选项是使用 LocalForward 关键字处理的,并且包含 Control{Master,Path} 行以确保您在建立隧道后拥有控制权。

然后,您可以将 bash 脚本修改为如下所示:

#!/bin/bash

if ! ssh -f -N otherHosttunnel; then
    echo "ERROR: couldn't start tunnel." >&2
    exit 1
else
    synergyc localhost
    ssh -O exit otherHosttunnel
fi

-f 选项使隧道处于后台状态,在 ControlPath 上留下一个套接字以便稍后关闭隧道。如果 ssh 失败(可能是由于网络错误或 ExitOnForwardFailure),则无需退出隧道,但如果它没有失败 (else),则 synergyc启动,然后隧道退出后关闭。

您可能还想查看 SSH 选项 LocalCommand 是否可用于从 ssh 配置文件中启动 synergyc

With all due respect to the users of pgrep, pkill, ps | awk, etc, there is a much better way.

Consider that if you rely on ps -aux | grep ... to find a process you run the risk of a collision. You may have a use case where that is unlikely, but as a general rule, it's not the way to go.

SSH provides a mechanism for managing and controlling background processes. But like so many SSH things, it's an "advanced" feature, and many people (it seems, from the other answers here) are unaware of its existence.

In my own use case, I have a workstation at home on which I want to leave a tunnel that connects to an HTTP proxy on the internal network at my office, and another one that gives me quick access to management interfaces on co-located servers. This is how you might create the basic tunnels, initiated from home:

$ ssh -fNT -L8888:proxyhost:8888 -R22222:localhost:22 officefirewall
$ ssh -fNT -L4431:www1:443 -L4432:www2:443 colocatedserver

These cause ssh to background itself, leaving the tunnels open. But if the tunnel goes away, I'm stuck, and if I want to find it, I have to parse my process list and home I've got the "right" ssh (in case I've accidentally launched multiple ones that look similar).

Instead, if I want to manage multiple connections, I use SSH's ControlMaster config option, along with the -O command-line option for control. For example, with the following in my ~/.ssh/config file,

host officefirewall colocatedserver
    ControlMaster auto
    ControlPath ~/.ssh/cm_sockets/%r@%h:%p

the ssh commands above, when run, will leave spoor in ~/.ssh/cm_sockets/ which can then provide access for control, for example:

$ ssh -O check officefirewall
Master running (pid=23980)
$ ssh -O exit officefirewall
Exit request sent.
$ ssh -O check officefirewall
Control socket connect(/home/ghoti/.ssh/cm_socket/[email protected]:22): No such file or directory

And at this point, the tunnel (and controlling SSH session) is gone, without the need to use a hammer (kill, killall, pkill, etc).

Bringing this back to your use-case...

You're establishing the tunnel through which you want syngergyc to talk to syngergys on TCP port 12345. For that, I'd do something like the following.

Add an entry to your ~/.ssh/config file:

Host otherHosttunnel
    HostName otherHost
    User otherUser
    LocalForward 12345 otherHost:12345
    RequestTTY no
    ExitOnForwardFailure yes
    ControlMaster auto
    ControlPath ~/.ssh/cm_sockets/%r@%h:%p

Note that the command line -L option is handled with the LocalForward keyword, and the Control{Master,Path} lines are included to make sure you have control after the tunnel is established.

Then, you might modify your bash script to something like this:

#!/bin/bash

if ! ssh -f -N otherHosttunnel; then
    echo "ERROR: couldn't start tunnel." >&2
    exit 1
else
    synergyc localhost
    ssh -O exit otherHosttunnel
fi

The -f option backgrounds the tunnel, leaving a socket on your ControlPath to close the tunnel later. If the ssh fails (which it might due to a network error or ExitOnForwardFailure), there's no need to exit the tunnel, but if it did not fail (else), synergyc is launched and then the tunnel is closed after it exits.

You might also want to look in to whether the SSH option LocalCommand could be used to launch synergyc from right within your ssh config file.

空心空情空意 2024-08-20 04:27:09

快速总结:行不通。

我的第一个想法是,您需要在后台启动进程以使用 $! 获取其 PID。

像这样的模式

some_program &
some_pid=$!
wait $some_pid

可能会满足您的需要...只不过 ssh 将不再在前台询问密码。

那么,您可能最终需要一些不同的东西。 ssh -f 可能会产生一个新进程,您的 shell 永远无法通过调用它来知道它。理想情况下,ssh 本身会提供一种将其 PID 写入某个文件的方法。

Quick summary: Will not work.

My first idea is that you need to start the processes in the background to get their PIDs with $!.

A pattern like

some_program &
some_pid=$!
wait $some_pid

might do what you need... except that then ssh won't be in the foreground to ask for passphrases any more.

Well then, you might need something different after all. ssh -f probably spawns a new process your shell can never know from invoking it anyway. Ideally, ssh itself would offer a way to write its PID into some file.

2024-08-20 04:27:09

刚刚遇到这个线程并想提及“pidof”linux 实用程序:

$ pidof init
1

just came across this thread and wanted to mention the "pidof" linux utility:

$ pidof init
1
世界如花海般美丽 2024-08-20 04:27:09

您可以使用 lsof 显示在本地主机上侦听端口 12345 的进程的 pid:

lsof -t -i @localhost:12345 -sTCP:listen

示例:

PID=$(lsof -t -i @localhost:12345 -sTCP:listen)
lsof -t -i @localhost:12345 -sTCP:listen >/dev/null && echo "Port in use"

You can use lsof to show the pid of the process listening to port 12345 on localhost:

lsof -t -i @localhost:12345 -sTCP:listen

Examples:

PID=$(lsof -t -i @localhost:12345 -sTCP:listen)
lsof -t -i @localhost:12345 -sTCP:listen >/dev/null && echo "Port in use"
雨的味道风的声音 2024-08-20 04:27:09

好吧,我不想添加 &在命令的末尾,因为如果控制台 wintow 关闭,连接将会终止...所以我最终得到了 ps-grep-awk-sed-combo

ssh -f -N -L localhost:12345:otherHost:12345   otherUser@otherHost
echo `ps aux | grep -F 'ssh -f -N -L localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/ssh
synergyc localhost
echo `ps aux | grep -F 'synergyc localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/synergy

(你可以将 grep 集成到 awk 中,但我现在太懒了)

well i dont want to add an & at the end of the commands as the connection will die if the console wintow is closed ... so i ended up with an ps-grep-awk-sed-combo

ssh -f -N -L localhost:12345:otherHost:12345   otherUser@otherHost
echo `ps aux | grep -F 'ssh -f -N -L localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/ssh
synergyc localhost
echo `ps aux | grep -F 'synergyc localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/synergy

(you could integrate grep into awk, but im too lazy now)

め七分饶幸 2024-08-20 04:27:09

您可以删除 -f,这会使其在后台运行,然后使用 eval 运行它并自行强制其在后台运行。

然后您可以获取 pid。确保将 & 放在 eval 语句中。

eval "ssh -N -L localhost:12345:otherHost:12345 otherUser@OtherHost & " 
tunnelpid=$!

You can drop the -f, which makes it run it in background, then run it with eval and force it to the background yourself.

You can then grab the pid. Make sure to put the & within the eval statement.

eval "ssh -N -L localhost:12345:otherHost:12345 otherUser@OtherHost & " 
tunnelpid=$!
怀念你的温柔 2024-08-20 04:27:09

另一种选择是使用 pgrep 查找最新 ssh 进程的 PID

ssh -fNTL 8073:localhost:873 otherUser@OtherHost
tunnelPID=$(pgrep -n -x ssh)
synergyc localhost
kill -HUP $tunnelPID

Another option is to use pgrep to find the PID of the newest ssh process

ssh -fNTL 8073:localhost:873 otherUser@OtherHost
tunnelPID=$(pgrep -n -x ssh)
synergyc localhost
kill -HUP $tunnelPID
浮世清欢 2024-08-20 04:27:09

这对于 synergyc(以及大多数其他尝试自我守护的程序)来说更是一个特例。使用 $!会起作用,只不过 synergyc 在执行期间执行了一个clone()系统调用,这将给它一个新的PID,而不是bash认为它拥有的PID。如果您想解决这个问题以便可以使用 $!,那么您可以告诉 synergyc 留在前台,然后将其设置为后台。

synergyc -f -n mydesktop remoteip &
synergypid=$!

synergyc 还执行一些其他操作,例如自动重启,如果您尝试管理它,您可能需要将其关闭。

This is more of a special case for synergyc (and most other programs that try to daemonize themselves). Using $! would work, except that synergyc does a clone() syscall during execution that will give it a new PID other than the one that bash thought it has. If you want to get around this so that you can use $!, then you can tell synergyc to stay in the forground and then background it.

synergyc -f -n mydesktop remoteip &
synergypid=$!

synergyc also does a few other things like autorestart that you may want to turn off if you are trying to manage it.

梦在深巷 2024-08-20 04:27:09

基于 @ghoti 的非常好的答案,这里有一个更简单的脚本(用于测试),利用 SSH 控制套接字,无需额外配置:

#!/bin/bash
if ssh -fN -MS /tmp/mysocket -L localhost:12345:otherHost:12345 otherUser@otherHost; then
    synergyc localhost
    ssh -S /tmp/mysocket -O exit otherHost
fi

synergyc 将仅启动如果隧道已成功建立,则一旦 synergyc 返回,隧道本身就会关闭。
尽管该解决方案缺乏适当的错误报告。

Based on the very good answer of @ghoti, here is a simpler script (for testing) utilising the SSH control sockets without the need of extra configuration:

#!/bin/bash
if ssh -fN -MS /tmp/mysocket -L localhost:12345:otherHost:12345 otherUser@otherHost; then
    synergyc localhost
    ssh -S /tmp/mysocket -O exit otherHost
fi

synergyc will be only started if tunnel has been established successfully, which itself will be closed as soon as synergyc returns.
Albeit the solution lacks proper error reporting.

⒈起吃苦の倖褔 2024-08-20 04:27:09

您可以使用以下行查找绑定到本地端口的 ssh 进程:

netstat -tpln | grep 127\.0\.0\.1:12345 | awk '{print $7}' | sed 's#/.*##'

它返回使用本地主机上的端口 12345/TCP 的进程的 PID。因此,您不必过滤来自 ps 的所有 ssh 结果。

如果您只需要检查该端口是否已绑定,请使用:

netstat -tln | grep 127\.0\.0\.1:12345 >/dev/null 2>&1

如果没有绑定,则返回 1;如果有人正在监听此端口,则返回 0

You could look out for the ssh proceess that is bound to your local port, using this line:

netstat -tpln | grep 127\.0\.0\.1:12345 | awk '{print $7}' | sed 's#/.*##'

It returns the PID of the process using port 12345/TCP on localhost. So you don't have to filter all ssh results from ps.

If you just need to check, if that port is bound, use:

netstat -tln | grep 127\.0\.0\.1:12345 >/dev/null 2>&1

Returns 1 if none bound or 0 if someone is listening to this port.

贪了杯 2024-08-20 04:27:09

这里有很多有趣的答案,但没有人提到 SSH 的联机帮助页确实描述了这种确切的情况! (参见TCP转发< /a> 部分)。他们提供的解决方案要简单得多:

ssh -fL 12345:localhost:12345 user@remoteserver sleep 10
synergyc localhost

现在详细介绍:

  1. 首先我们用隧道启动 SSH;感谢 -f 它将启动连接,然后才分叉到后台(与 ssh ... &; pid=$! 的解决方案不同,其中 ssh 被发送到后台并且下一个命令在创建隧道之前执行。在远程计算机上,它将运行 sleep 10 ,等待 10 秒然后结束。
  2. 10 秒内,我们应该启动所需的命令,在本例中为 synergyc localhost。它将连接到隧道,然后 SSH 就会知道该隧道正在使用中。
  3. 10 秒后,sleep 10 命令将完成。但隧道仍然被 Synergyc 使用,因此 SSH 不会关闭底层连接,直到隧道被释放(即直到 synergyc 关闭套接字)。
  4. 当 synergyc 关闭时,它将释放隧道,而 SSH 将自行终止,关闭连接。

这种方法的唯一缺点是,如果我们使用的程序由于某种原因将关闭并重新打开连接,那么 SSH 将在连接关闭后立即关闭隧道,并且该程序将无法重新连接。如果这是一个问题,那么您应该使用 @doak 的答案 中描述的方法,该方法使用控制套接字正确终止 SSH 连接,并使用 -f 确保当 SSH 分叉到后台时创建隧道。

There are many interesting answers here, but nobody mentioned that the manpage of SSH does describe this exact case! (see TCP FORWARDING section). And the solution they offer is much simpler:

ssh -fL 12345:localhost:12345 user@remoteserver sleep 10
synergyc localhost

Now in details:

  1. First we start SSH with a tunnel; thanks to -f it will initiate the connection and only then fork to background (unlike solutions with ssh ... &; pid=$! where ssh is sent to background and next command is executed before the tunnel is created). On the remote machine it will run sleep 10 which will wait 10 seconds and then end.
  2. Within 10 seconds, we should start our desired command, in this case synergyc localhost. It will connect to the tunnel and SSH will then know that the tunnel is in use.
  3. After 10 seconds pass, sleep 10 command will finish. But the tunnel is still in use by synergyc, so SSH will not close the underlying connection until the tunnel is released (i.e. until synergyc closes socket).
  4. When synergyc is closed, it will release the tunnel, and SSH in turn will terminate itself, closing a connection.

The only downside of this approach is that if the program we use will close and re-open connection for some reason then SSH will close the tunnel right after connection is closed, and the program won't be able to reconnect. If this is an issue then you should use an approach described in @doak's answer which uses control socket to properly terminate SSH connection and uses -f to make sure tunnel is created when SSH forks to the background.

旧城烟雨 2024-08-20 04:27:09

由于我是菜鸟,这里有一个解决方案,可能有点过头了,但效果很好:使用 tmux

  1. 在 tmux 中创建隧道会话:
tmux new -s my_tunnel ssh :8282:host02:22 -N user01@host01

如果您设置了 ssh 密钥,您甚至可以将 -d 添加到命令中,以告诉 tmux 随后立即分离。
(如果您有密码,则需要使用 Ctrl+b 后跟 d 手动分离)

  1. 您的隧道在后台运行,并链接了一个人类可读的名称到 tmux 会话:my_tunnel,您可以按如下方式登录:
tmux a -t my_tunnel

您可以使用:ssh user01@host01 -p 8282 或使用任何端口转发到 host01 通过它进行连接:来自 root 的 8282。

  1. 你可以简单地杀死它:
tmux kill-session -t my_tunnel

As I'm a noob, here is a solution that is probably an overkill, but works pretty well: use tmux

  1. Create your tunnel session in tmux:
tmux new -s my_tunnel ssh :8282:host02:22 -N user01@host01

If you set-up an ssh key, you can even add -d to the command to tell tmux to detach immediately afterward.
(if you have a password, then you'll need to detach manually using Ctrl+b followed by d)

  1. your tunnel runs in the background with a human-readable name linked to the tmux session : my_tunnel that you can log to as follows:
tmux a -t my_tunnel

you can connect through it using : ssh user01@host01 -p 8282 or using any port forwarding to host01:8282 from your rooter.

  1. you can kill it as simply as:
tmux kill-session -t my_tunnel
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文