TmuxでSSHセッションを量産するツールを作った

tmuxを使っていると、今開いているSSHセッションを維持したままペインもしくはウィンドウを増やしたいことがある。たとえば、SSH先でviを開いてしまったが、同じホストで別のコマンドを実行したくなることはよくある。このような場合、viを1度閉じるか、新しいペインを分割して再度SSHコマンドを実行するしかないのだが、どちらにせよ若干の手間がかかる。サーバ管理をしているとこのようなことがよく発生するので、現在のSSHセッションを複製するための簡単なツールを作った。

スクリーンショット
SSH - A SSH session duplicator for tmux

実行ファイルは単なるシェルスクリプトなのでパスの通ったディレクトリに設置するだけでよい。環境によってはpgrepのインストールが別途必要となるかもしれない。

名前はいわゆる「cdd」のSSH版ということで「sshh」にした。引数の扱いもほぼ同じで、たとえばsshh 1というコマンドを実行すると、同一セッションの1番ウィンドウで実行中のSSHコマンドを現在のペインで実行する。ペイン指定したい場合はssh 1,2のようにすると、1番ウィンドウの2番ペインを指定できる。ペインが指定されなかった場合のデフォルト値は0番になる。本当であれば「1つ前に選択していたペイン」をデフォルトにしたいところではあるが、現在のところ値を取得する手段がない。なお、指定されたウィンドウもしくはペインで実行されているコマンドがSSHでない場合も、そのコマンドを現在のペインで実行することが出来る。この場合は実行前に確認のプロンプトが表示される。

このコマンドは単体で使うよりも、tmuxのショートカットキーに割り当てることで効果を発揮する。単体で使用する場合、コマンドヒストリーを「SSH」で検索する代わりにsshhコマンドを実行するだけなので、大した省力化は実現できない。ショートカットキーを設定する場合は、たとえば.tmux.confに以下のように記述する。

# C-sで縦分割したペインにSSHセッションを複製する
bind-key C-s run-shell "tmux split-window -h \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

# C-wで横分割したペインにSSHセッションを複製する
bind-key C-w run-shell "tmux split-window -v \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

# C-nで新規ウィンドウにSSHセッションを複製する
bind-key C-n run-shell "tmux new-window \"SSHH_INDEX=$(tmux display -p \"#{window_index},#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

上記の設定の場合、生成されたウィンドウもしくはペインはZSHで起動される。好みのシェルに合わせて、必要に応じてbashなどに置き換える必要がある。

なお、この例ではsshhコマンドの実行はsend-keysを用いて行われているため、ユーザーが手でコマンドを入力した場合と変わらない動作になる。言い換えると、SSHセッション終了後は、普通にSSHコマンドを終了したときのように、シェルに制御が戻る。用途によってはSSHの終了時にペインも閉じてしまいたいこともあるかもしれない。その場合は次のように書いておくと、他にペインが存在しない場合を除いて自動でペインを終了することが出来る。

bind-key C-s run-shell "tmux split-window -h \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}; if [ "$(tmux list-panes | wc -l)" -gt 1 ]; then exit; fi' ENTER

bind-key C-w run-shell "tmux split-window -v \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}; if [ "$(tmux list-panes | wc -l)" -gt 1 ]; then exit; fi' ENTER