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


101

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

为了避免在不可避免的Internet连接断开时失去对运行过程的控制,我始终在tmux会话中进行工作。我想要一种在每次SSH连接启动时自动连接tmux的方法,因此我不必总是tmux attach || tmux new在SSH输入后键入。

不幸的是,这并没有像我最初希望的那么简单。

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

这仍然是一个受欢迎的问题。我想指出,自5年前以来,tmux发生了很大变化。较高的答案并不反映最佳实践,即根本不通过外壳执行此操作,而是通过~/.ssh/config。因此,大多数人可能需要的答案是stackoverflow.com/a/52838493/5354137
2013年6

Answers:


95

服务器端配置:

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

if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]]; then
  tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux
fi

ssh_tmux如果不存在,此命令将创建一个称为tmux的会话,或者重新连接到具有该名称的现有会话。万一您的连接断开或几周前忘记了会话,每次SSH登录都会自动将您带回到您留下的tmux-ssh会话。

从您的客户连接:

没什么特别的ssh user@hostname


4
我一直在寻找它,我前段时间也使用了一段非常类似于您的代码,但是会话是用户名(更改ssh_tmux$USER
Iacchus

4
有关对vs的有用评论,请参阅moneytoo的答案$SSH_TTY$SSH_CONNECTION
陶先生

2
您可以使用它tmux new-session -A -s ssh_tmux来替换tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux更短-A的会话,如果更令人困惑,则告诉tmux附加会话(如果该会话已经存在)
Gradient

3
为了避免破坏“ scp”,您还需要检查这是否是交互式外壳程序:if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]];
janfrode

3
@janfrode不要依赖$PS1[[ $- == *i* ]]而是使用它,因为即使PS1不是交互式外壳,也可以定义它。
恩里科

56

好吧,我找到了一个最令人满意的解决方案。在本地~/.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 Forcetmux命令,并且是该参数所必需的。)


22
如果您的tmux支持版本,请考虑使用tmux new -A foo它将附加到已命名的现有会话上foo,并在必要时创建它。这使您可以简化功能/usr/bin/ssh -t "$@" tmux new -A(并务必引用$@!)。
chepner 2014年

2
注意:如果您定期连接的某些计算机未安装tmux,则可能要说一句function ssht类似的话,以便您可以继续ssh正常使用。否则,/usr/bin/ssh只要连接到没有tmux的机器,只要在命令提示符下键入即可:)
Alex Ryan

1
如果您很懒,则可以使用ssht连接到远程tmux会话。OS X用户可以通过brew点击它,而Linux用户可以通过fpm使用该Makefile创建一个包,或者直接复制ssht~/bin
brejoc

1
哈哈,真好!对我而言,将这个bash单行代码与Makefiles和brew一起包装在整个Github存储库中似乎有点矫kill过正,但是嘿,越容易越好!
亚历克斯·瑞安

1
解决了:ssh -t user@hostname "LANG=$LANG tmux attach || tmux new"
alecdwm

23

连接:

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

会议期间:

使用Ctrl+d完成会话(TMUX窗口关闭)或Ctrl+b d临时分离的会话,并连接到它以后再。

记得!如果服务器重启会话丢失!

随时在tmux内时,您都可以Ctrl+b s用来查看会话列表并将当前切换到另一个。

修复您的.bashrc:

我建议您在以下代码中定义通用功能.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会话相对应的临时session-id,请使用tmux id:

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

1
避免||在某些情况下使用的一种巧妙技巧是将include包含在内new-session.tmux.conf并且始终使用tmux a -t 0
Florian Wendelborn 2015年

4
在较新版本的tmux中,您也可以使用tmux new-session -A它将附加的(如果存在),否则将创建一个新的。
Dragon788

18

远程计算机上的tmux 3.1或更高版本¹

放入您的本地~/.ssh/config,放²:

Host myhost
  Hostname host
  User user
  RequestTTY yes
  RemoteCommand tmux new -A -s foobar

无关,但是如果您要处理非ASCII字符,我建议将其更改tmux -u …为即使在未设置适当环境变量的机器上也可以显式启用Unicode支持。

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

与上面几乎相同,但是将最后一行更改为³:

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

¹自2020年10月29日起,tmux 3.1或更高版本附带的发行版列表已经很长了。

²new的缩写new-session

³at的缩写attach-session


使用遥控器的authorized_keys文件的另一种方法:

如果您~/.ssh/config出于某种原因不想拥有文件,或者希望远程计算机强制连接的计算机连接到/打开会话,请将其添加到远程计算机中~/.ssh/authorized_keys

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

当然,这将在所有安装了相应私钥的客户端上起作用,具体取决于您想要的是上行还是下行。如果出现问题,则存在无法连接的风险。


为什么要tmux at代替tmux a?同样明智的是,为此使用命名会话,否则tmux将在登录到主机后附加到“随机”的现有会话上。
埃里克

您如何暂停tmux会话?ssh击中后进入kinda边缘状态Ctrl+A Ctrl+Z
埃里克

它只是断开连接。就我而言,这就是我期望并满意的行为。
6

1
Ctrl-B D与作品比较Ctrl-B Ctrl-Z。谢谢!
Eric

1
恕我直言,这应该是投票最多的答案。我正在寻找(2)。
cduguet

17

我使用了@kingmeffisto的行(不允许评论该答案),并且添加了退出,因此终止tmux也会终止ssh连接。但是,这中断了SFTP会话,因此我必须检查$SSH_TTY而不是$SSH_CONNECTION

编辑4/2018:添加了对交互式终端的测试,[[ $- =~ i ]]以允许诸如Ansible之类的工具正常工作。

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

15

本博客文章所述,您可以使用单个命令ssh并将其附加到现有的tmux会话:

ssh hostname -t tmux attach -t 0

1
这就是我的答案(尽管我tmux attach || tmux new这样做是为了不为每个连接都创建一个新的tmux会话)。棘手的部分是正确的命令是ssh -t user@host tmux attach || tmux new唯一的别名,而在命令字符串中需要参数的别名的唯一方法就是创建一个新函数,就像我上面所做的那样。
Alex Ryan

我知道,但有些人(例如我)可能更喜欢一种不涉及定义功能的
单线

3
这将连接到名为“ 0”的会话。也就是说,通常的格式是ssh [hostname] -t tmux attach -t [sessionName]
David Doria,2016年

1
这对我来说真的很好。组合在一起将成为unix.stackexchange.com/a/116674 ..现在,我的腻子GUI看起来像这样。imgur.com/uFhxN30。我可以使用Cntrl + b + d断开会话。非常简单和方便
。–

2

byobu是tmux / screen的不错的有用包装器。连接到现有会话(如果存在)或创建一个新会话。

我将其与autossh一起使用,它可以正常地重新连接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'
}

2

您可能会发现这很有用-在循环中使用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

2

这实际上可以创造出出色的用户体验。每当您打开终端时(无论是物理上还是ssh),它都会自动启动tmux。您可以在一台设备上开始工作,退出终端,然后在另一台设备上继续工作。如果它检测到已经连接到该会话的某人,它将创建新会话。把它放在服务器上,取决于你的shell~/.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

0

我知道我正在恢复旧线程,但是我已经在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在列表客户端上失败的错误。


0

如果您的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

0

追加到远程服务器的底部~/.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结束后能干净地回家。

谢谢大家。


注意,/etc/.bashrc.shared在用户和root的末尾都提供了一个共享,.bashrc用于两者中使用的常见内容,例如colorized ls,各种别名,函数和路径扩展,即我不希望root / .bashrc和user中都没有冗余代码/.bashrc。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.