Как автоматически запускать tmux в сеансе SSH?

92

У меня есть около десяти серверов, к которым я регулярно подключаюсь по SSH. У каждого есть запись в ~/.ssh/configфайле на моем локальном компьютере .

Чтобы не потерять контроль над моим запущенным процессом, когда мое интернет-соединение неизбежно разрывается, я всегда работаю внутри tmuxсеанса. Я хотел бы, чтобы tmux автоматически подключался каждый раз при запуске SSH-соединения, поэтому мне не нужно всегда вводить tmux attach || tmux newтекст после входа в SSH.

К сожалению, это оказалось не так просто, как я изначально надеялся.

  • Я не хочу добавлять какие-либо команды ~/.bashrcна серверы, потому что мне это нужно только для сеансов SSH, а не для локальных сеансов.
  • Добавление tmux attach || tmux newк ~/.ssh/rcна серверах приводит просто ошибка not a terminalброска после подключения, даже если RequestTTY forceопция добавляется к строке для этого сервера в моем локальном файле SSH конфигурации.
Алекс Райан
источник
1
Поскольку это по-прежнему популярный вопрос, и в нем конкретно упоминается ~/.ssh/config: большинство из вас, вероятно, ищут не один из первых пяти ответов, а шестой ( stackoverflow.com/a/52838493/5354137 ). С любой достаточно свежей tmuxверсией это также самый разумный способ делать что-то.
Sixtyfive,

Ответы:

90

Конфигурация на стороне сервера:

Чтобы автоматически запускать tmux на вашем удаленном сервере при обычном входе в систему через SSH (и только SSH), отредактируйте ~/.bashrcвашего пользователя или root (или обоих) на удаленном сервере соответственно:

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

Эта команда создает сеанс tmux, который вызывается, ssh_tmuxесли его нет, или повторно подключается к уже существующему сеансу с этим именем. В случае разрыва соединения или если вы забыли сеанс несколько недель назад, каждый вход в систему по SSH автоматически возвращает вас к сеансу tmux-ssh, который вы оставили.

Подключитесь от вашего клиента:

Ничего особенного, просто ssh user@hostname.

Kingmeffisto
источник
4
Я искал это, также я использовал фрагмент кода, очень похожий на ваш некоторое время назад, но в сеансе было имя пользователя (изменение ssh_tmuxна $USER)
Iacchus
3
См 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
2
@janfrode не полагается $PS1, используйте [[ $- == *i* ]]вместо этого, поскольку PS1 может быть определен, даже если это не интерактивная оболочка.
Enrico
53

Хорошо, я нашел в основном удовлетворительное решение. В моем локальном ~/.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 и необходим для нее.)

Алекс Райан
источник
22
Если ваша версия tmuxподдерживает его, рассмотрите возможность использования tmux new -A foowhich, fooесли возможно, будет подключаться к существующему сеансу с именем , создавая его при необходимости. Это позволяет вам упростить вашу функцию до /usr/bin/ssh -t "$@" tmux new -A(и обязательно цитировать $@!).
chepner
1
Примечание: если на некоторых машинах, к которым вы регулярно подключаетесь, не установлен tmux, вы можете сказать function sshtили что-то подобное, чтобы вы могли продолжать использовать в sshобычном режиме. В противном случае просто вводите /usr/bin/sshв командной строке всякий раз, когда подключаетесь к машине без tmux :)
Alex Ryan
1
Если вы ленивы, вы можете просто использовать ssht для подключения к удаленным сеансам tmux. Пользователи OS X могут использовать его через brew, а пользователи Linux могут создать пакет через fpm с этим Makefile или просто скопировать sshtв него ~/bin.
Brejoc
1
Ахаха прикольно! Мне кажется излишним обернуть этот bash однострочник во все репозиторий Github с Makefiles, brew и тому подобным, но, эй, чем проще, тем лучше!
Alex Ryan
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, используйте его как идентификатор tmux :

SID=$USER-$BASHPID
ssh user@host -t "tmux new-session -s $SID || tmux attach-session -t $SID"
Денис Колодин
источник
1
Аккуратный трюк , чтобы избежать этого ||в некоторых потребительных случаях должна включать new-sessionв .tmux.confи просто всегда использовать tmux a -t 0.
Флориан Вендельборн,
4
В более новых версиях tmux вы также можете использовать, tmux new-session -Aкоторый будет прикреплен, если он существует, иначе он создаст новый.
dragon788 02
15

Я использовал строки из @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
деньги
источник
14

Как описано в этом сообщении блога, вы можете использовать ssh, а затем подключиться к существующему сеансу tmux с помощью одной команды:

ssh hostname -t tmux attach -t 0
Фабиан Педрегоса
источник
Это то, что делает мой ответ (хотя я использую, tmux attach || tmux newчтобы новый сеанс tmux не создавался для каждого соединения). Сложность заключается в том, что это правильная команда, ssh -t user@host tmux attach || tmux newи единственный способ присвоить псевдониму то, что требует аргумента внутри командной строки, - это создать новую функцию, как я сделал выше.
Alex Ryan
Я знаю, но некоторые люди (например, я) могут предпочесть
однострочный текст,
3
Это подключается к сеансу под названием «0». То есть общая формаssh [hostname] -t tmux attach -t [sessionName]
Дэвид Дориа
1
У меня это сработало очень хорошо ... В совокупности это будет unix.stackexchange.com/a/116674 ... так что теперь мой графический интерфейс с замазкой выглядит так ... imgur.com/uFhxN30 . Я могу отключить сеансы с помощью Cntrl + b + d. Очень просто и удобно ..
alpha_989 03
14

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 переходит в состояние неопределенности Ctrl+A Ctrl+Z.
Эрик
Он просто отключается. Насколько я понимаю, это поведение, которого я ожидал и которым я доволен.
Sixtyfive,
1
Ctrl-B Dработает лечить по сравнению с Ctrl-B Ctrl-Z. Благодарность!
Эрик
1
Это должен быть, imho, самый популярный ответ. Искал именно (2).
cduguet
1

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'
}
Сандип
источник
1

Вы можете найти это полезным - использует 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
Нил МакГилл
источник
1

Это тот, который на самом деле создает отличный пользовательский опыт. Он автоматически запускает tmux всякий раз, когда вы открываете терминал (как физически, так и ssh). Вы можете начать работу на одном устройстве, выйти из терминала и продолжить работу на другом. Если он обнаружит, что кто-то уже подключен к сеансу, он создаст новый сеанс. Ставьте на сервер , в зависимости от вашей оболочки ~/.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

Этот способ позволяет повторно подключиться к старому экземпляру tmux, если ваш сеанс ssh прерывается. 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
эриккуртин
источник