【问题标题】:How to automatically start tmux on SSH session?如何在 SSH 会话上自动启动 tmux?
【发布时间】:2022-02-25 07:37:25
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这仍然是一个热门问题。我想指出,自五年前以来,tmux 发生了很大变化。更高层的答案并没有反映完全不通过 shell 执行此操作的最佳实践,而是通过~/.ssh/config。因此,大多数人可能需要的答案是stackoverflow.com/a/52838493/5354137

标签: ssh tmux


【解决方案1】:

服务器端配置:

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

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

【讨论】:

  • 我在找这个,前段时间我也用了一段和你很相似的代码,但是会话是用户名(将ssh_tmux更改为$USER
  • 查看 moneytoo 的 answer 以获得对 $SSH_TTY$SSH_CONNECTION 的有用评论。
  • 你可以用tmux new-session -A -s ssh_tmux 代替tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux 更短,如果有点混乱,-A 告诉 tmux 如果会话已经存在就附加它
  • 为了避免破坏“scp”,你还需要检查这是否是一个交互式shell:if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]];
  • @janfrode 不要依赖$PS1,而是使用[[ $- == *i* ]],因为即使 PS1 不是交互式 shell,也可以定义。
【解决方案2】:

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

【讨论】:

  • 如果您的tmux 版本支持它,请考虑使用tmux new -A foo,如果可能,它将附加到一个名为foo 的现有会话,并在必要时创建它。这使您可以将函数简化为/usr/bin/ssh -t "$@" tmux new -A(并确保引用$@!)。
  • 注意:如果你经常连接的一些机器没有安装tmux,你可能想说function ssht之类的,这样你就可以继续正常使用ssh。否则,只要连接到没有 tmux 的机器,只需在命令提示符下键入 /usr/bin/ssh :)
  • 如果你比较懒惰,你可以使用ssht 连接到你的远程 tmux 会话。 OS X 用户可以tap it via brew,Linux 用户可以通过fpm 使用Makefile 创建一个包,或者简单地将ssht 复制到~/bin
  • 哈哈不错!对我来说,用 Makefile 和 brew 等将这个 bash 单线包装在整个 Github 存储库中似乎有点矫枉过正,但是嘿,越简单越好!
  • 已解决:ssh -t user@hostname "LANG=$LANG tmux attach || tmux new"
【解决方案3】:

不要在服务器端这样做!

(这可能很危险,因为您最终可能会被远程计算机锁定)。相反,像这样使用~/.ssh/config

远程机器上的 tmux 3.1 或更新版本¹

到您当地的~/.ssh/config,输入²:

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.0a 或更早版本

和上面差不多,但是把最后一行改成³:

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

¹repology.org 有一个发行版列表及其 tmux 版本

²newnew-session的缩写。

³ atattach-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 会在登录主机时附加到“随机”现有会话。
  • Ctrl-B DCtrl-B Ctrl-Z 相比更有效。谢谢!
  • 这应该是,恕我直言,投票最多的答案。我正在寻找 (2)。
  • @kyb,这是与RequestTTY yes 等效的命令行开关... 后者是否出现在您的~/.ssh/config 条目中?
  • 这适用于独立服务器,但不幸的是,当与 Host * 一起使用时,它会使 git push(使用 ssh)停止工作:Cannot execute command-line and remote command. fatal: Could not read from remote repository.
【解决方案4】:

连接:

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 会话相对应的 临时会话 id,请使用 tmux id:

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

【讨论】:

  • 在某些用例中避免|| 的巧妙技巧是在.tmux.conf 中包含new-session 并始终使用tmux a -t 0
  • 在较新版本的 tmux 中,您还可以使用tmux new-session -A,如果存在则附加,否则将创建一个新版本。
【解决方案5】:

我使用了来自@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

【讨论】:

    【解决方案6】:

    this blog post 中所述,您可以使用 ssh 连接到现有的 tmux 会话,只需一条命令即可:

    ssh hostname -t tmux attach -t 0
    

    【讨论】:

    • 这就是我的答案(尽管我使用 tmux attach || tmux new 以便不会为每个连接创建一个新的 tmux 会话)。棘手的部分是正确的命令是ssh -t user@host tmux attach || tmux new,而在命令字符串中为需要参数的东西设置别名的唯一方法是创建一个新函数,就像我在上面所做的那样。
    • 我知道,但有些人(比如我)可能更喜欢不涉及定义函数的单行代码
    • 这会连接到一个名为“0”的会话。即一般形式为ssh [hostname] -t tmux attach -t [sessionName]
    • 这对我来说真的很好用..结合这将unix.stackexchange.com/a/116674..所以现在我的腻子GUI看起来像这样..imgur.com/uFhxN30。我可以使用 Cntrl + b + d 断开会话。非常简单方便..
    【解决方案7】:

    这是真正创造出色用户体验的一种。 每当您打开终端(物理和 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
    

    【讨论】:

      【解决方案8】:

      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'
      }
      

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

          我有以下解决方案,可让您连接到两台 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
          

          【讨论】:

            【解决方案11】:

            我知道我正在恢复一个旧线程,但我已经在 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 在列表客户端上失败的错误之外,它似乎工作得非常可靠。

            【讨论】:

              【解决方案12】:

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

              【讨论】:

                【解决方案13】:

                附加到远程服务器的底部~/.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 我认为更好。

                而且我喜欢添加一些 cmets 来帮助这个老家伙记住发生了什么而无需查找它。

                最后,我喜欢结尾处的 exit,让我在完成后干净利落地回家。

                谢谢大家。


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

                【讨论】:

                  【解决方案14】:

                  This guys script works great。只需将 bashrc-tmux 文件复制到 ~/.bashrc-tmux 并在 PS1 检查部分之后从 ~/.bashrc 获取它。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-02-02
                    • 1970-01-01
                    • 2015-05-07
                    • 2021-10-09
                    • 2023-03-22
                    相关资源
                    最近更新 更多