如何在 SSH 会话上自动启动 tmux?

我有10个左右的服务器,我连接到 SSH 定期。每个都在我的本地计算机的 ~/.ssh/config文件中有一个条目。

为了避免当我的互联网连接不可避免地下降时失去对运行过程的控制,我总是在 tmux会话内工作。我想有一种方法,让 tmux 自动连接每次 SSH 连接启动,所以我不必总是键入 tmux attach || tmux new后,我 SSH。

不幸的是,事情并不像我最初希望的那样简单。

  • 我不希望向服务器上的 ~/.bashrc添加任何命令,因为我只希望它用于 SSH 会话,而不是本地会话。
  • tmux attach || tmux new添加到服务器上的 ~/.ssh/rc只会导致在连接之后抛出错误 not a terminal,即使在本地 SSH 配置文件中将 RequestTTY force选项添加到该服务器的行中也是如此。
75142 次浏览

好的,我找到了一个非常令人满意的解决方案。在我的本地 ~/.bashrc中,我写了一个函数:

function ssh () {/usr/bin/ssh -t "$@" "tmux attach || tmux new";}

它基本上覆盖 ssh 终端函数,以使用给定的参数调用内置的 ssh 程序,然后是 "tmux attach || tmux new"

($@表示命令行中提供的所有参数,因此 ssh -p 123 user@hostname将扩展为 ssh -t -p 123 user@hostname "tmux attach || tmux new")

(-t参数等效于 RequestTTY Force,是 tmux 命令所必需的。)

这篇博文中所述,您可以使用 ssh,然后使用一个命令附加到现有的 tmux 会话:

ssh hostname -t tmux attach -t 0

连接:

ssh user@host -t "tmux new-session -s user || tmux attach-session -t user"

会议期间:

从会话中使用 Ctrl+d结束会议(tmux 窗口关闭)或者从 Ctrl+b d暂时分离暂时分离,稍后再连接到它。

记住! 如果您的服务器重新启动会话丢失!

当你在多路混频的任何时候,你可以使用 Ctrl+b s看到 会议名单和切换电流到另一个。

修正你的错误:

我建议你在 .bashrc中定义通用函数:

function tmux-connect {
TERM=xterm-256color ssh -p ${3:-22} $1@$2 -t "tmux new-session -s $1 || tmux attach-session -t $1"
}

它默认使用 22端口。也定义快速连接别名:

alias office-server='tmux-connect $USER 192.168.1.123'
alias cloud-server='tmux-connect root my.remote.vps.server.com 49281'

无密码登入:

如果你不想每次都输入密码,那就生成 .ssh键到 自动登入:

ssh-keygen -t rsa
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_rsa

将您的公钥放到远程主机上:

ssh-copy-id -p <port> user@hostname

附加提示:

如果您想使用与本地 bash 会话对应的 临时会话 ID作为 TMux id:

SID=$USER-$BASHPID
ssh user@host -t "tmux new-session -s $SID || tmux attach-session -t $SID"

Byobu 是 tmux/screen 的一个非常有用的包装器。

我将它与 自动驾驶一起使用,自动驾驶可以优雅地重新连接 ssh 会话。强烈建议在间歇性连接问题的情况下使用它。

function ssh-tmux(){
if ! command -v autossh &> /dev/null; then echo "Install autossh"; fi
autossh -M 0 $* -t 'byobu || {echo "Install byobu-tmux on server..."} && bash'
}

服务器端配置:

要在通常通过 SSH (只有 SSH)登录时自动启动远程服务器上的 tmux,请相应地编辑远程服务器上用户或 root 用户(或两者)的 ~/.bashrc:

if [[ $- =~ i ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_TTY" ]]; then
tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux
fi

这个命令创建一个名为 ssh_tmux的 tmux 会话(如果不存在的话) ,或者重新连接到一个已经存在的会话(名称为 ssh_tmux)。万一您的连接中断或者几周前忘记了一个会话,每个 SSH 登录都会自动将您带回到您留下的 tmux-SSH 会话。

联系你的客户:

没什么特别的,就是 ssh user@hostname

我使用了@kingmeffisto 中的行(我不允许注释这个答案) ,并添加了一个出口,因此终止 tmux 也将终止 ssh 连接。然而,这破坏了 SFTP 会话,因此我必须检查 $SSH_TTY而不是 $SSH_CONNECTION

编辑4/2018: 通过 [[ $- =~ i ]]增加了交互式终端的测试,以允许像安赛尔这样的工具工作。

if [ -z "$TMUX" ] && [ -n "$SSH_TTY" ] && [[ $- =~ i ]]; then
tmux attach-session -t ssh || tmux new-session -s ssh
exit
fi

您可能会发现这很有用——在循环中使用 ssh 并重新连接到或连接到现有的 tmux 会话,这样您就有了一个非常简单的可靠性 网络中断后重新连接的方法

#!/bin/bash
#
# reconnect to or spawn a new tmux session on the remote host via ssh.
# If the network connection is lost, ssh will reconnect after a small
# delay.
#


SSH_HOSTNAME=$1
TMUX_NAME=$2
PORT=$3


if [[ "$PORT" != "" ]]
then
PORT="-p $PORT"
fi


if [ "$TMUX_NAME" = "" ]
then
SSH_UNIQUE_ID_FILE="/tmp/.ssh-UNIQUE_ID.$LOGNAME"


if [ -f $SSH_UNIQUE_ID_FILE ]
then
TMUX_NAME=`cat $SSH_UNIQUE_ID_FILE`
TMUX_NAME=`expr $TMUX_NAME + $RANDOM % 100`
else
TMUX_NAME=`expr $RANDOM % 1024`
fi


echo $TMUX_NAME > $SSH_UNIQUE_ID_FILE


TMUX_NAME="id$TMUX_NAME"
fi


echo Connecting to tmux $TMUX_NAME on hostname $SSH_HOSTNAME


SLEEP=0
while true; do


ssh $PORT -o TCPKeepAlive=no -o ServerAliveInterval=15 -Y -X -C -t -o BatchMode=yes $SSH_HOSTNAME "tmux attach-session -t $TMUX_NAME || tmux -2 -u new-session -s $TMUX_NAME"
SLEEP=10
if [ $SLEEP -gt 0 ]
then
echo Reconnecting to session $TMUX_NAME on hostname $SSH_HOSTNAME in $SLEEP seconds
sleep $SLEEP
fi
done

我知道我正在重新启用一个旧线程,但我已经在 bashrc 解决方案上做了一些工作,我认为它有一些用处:

#attach to the next available tmux session that's not currently occupied
if [[ -z "$TMUX" ]] && [ "SSH_CONNECTION" != "" ];
then
for i in `seq 0 10`; do #max of 10 sessions - don't want an infinite loop until we know this works
SESH=`tmux list-clients -t "$USER-$i-tmux" 2>/dev/null` #send errors to /dev/null - if the session doesn't exist it will throw an error, but we don't care
if [ -z "$SESH" ] #if there's no clients currently connected to this session
then
tmux attach-session -t "$USER-$i-tmux" || tmux new-session -s "$USER-$i-tmux" #attach to it
break #found one and using it, don't keep looping (this will actually run after tmux exits AFAICT)
fi #otherwise, increment session counter and keep going
done


fi

现在有一个10(11)会话的上限-我不想用 bashrc 中的无限循环杀死我的服务器。它似乎工作相当可靠,除了列表客户端上的 tmux 失败的错误,如果会话不存在。

不要在服务器端这样做!

这有潜在的危险,因为您可能最终被锁定在远程计算机之外。而且也不需要 shell 代码/别名等等。

相反..。

... 像这样使用 ~/.ssh/config:

远程计算机上的 tmux 3.1或 newer1

进入你的本地 ~/.ssh/config,put2:

Host myhost
Hostname host
User user
RequestTTY yes
RemoteCommand tmux new -A -s foobar
  • 正如@thiagowfx 所指出的,这样做的副作用是使它不能使用,例如 ssh myhost ls /tmp,因此不应该与 Host *一起使用... 我喜欢做的是有一个 Host myhost RemoteCommand tmux ...,然后除此之外,我将有一个 Host MYHOST没有它。
  • 你可以用 -t开关调用 ssh而不是 RequestTTY yes; 谢谢@kyb。
  • 偏离主题,但是如果您处理的是非 ASCII 字符,我建议将其更改为 tmux -u …,以便即使在没有设置适当环境变量的机器上也显式启用 Unicode 支持。

远程计算机上的 tmux 3.0 a 或更高版本

几乎和上面一样,但是把最后一行改为3:

  RemoteCommand tmux at -t foobar || tmux new -s foobar

1 Repology.org有一个发行版及其 tmux 版本的列表

2 newnew-session的简称。

3 atattach-session的简称。


除非,出于某种原因,你真的,真的不能在客户端做到这一点:

使用遥控器的 authorized_keys文件

如果出于某种原因不想要 ~/.ssh/config文件,或者希望远程计算机强制连接计算机连接到/打开会话,请将以下内容添加到远程 ~/.ssh/authorized_keys:

command="tmux at -t foobar || tmux new -s foobar" pubkey user@client

当然,这将从安装了相应私钥的 所有客户端开始工作,有些人可能会认为这是一个好的方面—— 但是: 如果出现任何问题,没有(半)物理访问机器就不可能再连接了!

这是一个实际上创建一个伟大的用户体验。 只要打开终端(物理上和 ssh) ,它就会自动启动 tmux。 您可以在一个设备上开始工作,退出终端,然后在另一个设备上继续工作。如果它检测到有人已经连接到会话,它将创建新的会话。 把它 在服务器上,取决于你的外壳 ~/.zshrc~/.bashrc

 if [[ -z "$TMUX" ]] ;then
ID="$( tmux ls | grep -vm1 attached | cut -d: -f1 )" # get the id of a deattached session
if [[ -z "$ID" ]] ;then # if not available attach to a new one
tmux new-session
else
tmux attach-session -t "$ID" # if available attach to it
fi
fi

这种方法允许您在 ssh 会话丢失时重新连接到旧的 tmux 实例。exec当然会保存一个叉子。

if [ -z "$TMUX"  ]; then
pid=$(tmux ls | grep -vm1 "(attached)" | cut -d: -f1)
if [ -z "$pid" ]; then
tmux new -d -s $pid
fi


exec tmux attach -t $pid
fi

附加到远程服务器的 ~/.bashrc (或可能是其 /etc/.bashrc.shared(1)):

# ======================== PUT THIS LAST IN .BASHRC ==========================
# --- If we're run by SSH, then auto start `tmux` and possibly re-attach user.
#       $-         interactive only via current option flags
#       -z $TMUX   no tmux nesting
#       $SSH_TTY   SSH must be running, and in a shell
#
if [[ $- == *i* ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_TTY" ]];  then
tmux attach-session -t "$USER"  || tmux new-session -s "$USER" && exit
fi

这里结合了很多好的技巧,比如 $-$SSH_TTY,我觉得更好。

我喜欢添加一些评论,帮助这个老家伙记住正在发生的事情,而不必查阅它。

最后,我喜欢 exit在结束时干净地回家,当我完成。

谢谢大家。


注意 我在用户和 root 的 .bashrc的末尾都提供了一个共享的 /etc/.bashrc.shared,用于两者中常用的东西,比如彩色的 ls、各种别名、函数和路径扩展,也就是说,我不希望在 root/中有多余的代码。Bashrc 也不是用户/。巴希尔。

这个家伙的脚本工程伟大的 。只需将 Bashrc-tmux文件复制到 ~/. bashrc-tmux,并在 PS1检查部分之后从 ~/. bashrc获取源文件。

我有以下解决方案,可以让您连接到两个 SSH 主机: 一个带 tmux,一个没有:

# Common rule that 1) copies your tmux.conf 2) runs tmux on the remote host
Host *-tmux
LocalCommand scp %d/.tmux.conf %r@%n:/home/%r/
RemoteCommand tmux new -As %r
RequestTTY yes
PermitLocalCommand yes


# Just connect.
# Notice the asterisk: makes possible to re-use connection parameters
Host example.com*
HostName example.com
User login


# Connect with tmux
Host example.com-tmux
HostKeyAlias dev.dignio.com