Heavy Watal

tmux — 仮想端末でリモート仕事を安全に

https://tmux.github.io/

GNU screen の後を継ぐ端末多重化ソフト(terminal multiplexer)。

1つの画面の中でウインドウを追加・分割して複数の端末を開く
GUIアプリのタブ代わりに。
1つのsshセッションで複数の端末を持てる。
ssh切断後も端末丸ごと継続され、後でまた繋ぎ直せる
不意のssh切断でも作業が失われない。
別の端末から接続しても同じ作業を継続できる。
nohup とかバックグラウンド化とか考えるより楽チン。

Homebrew で一発インストール: brew install tmux

キーバインド

tmux 内で prefix key に続けて特定のキーを送信すると、 そのキーに応じたさまざまなコマンドを実行できる (e.g. prefix? でキーバインドを列挙)。 prefix keyはデフォルトで C-b (controlbの略記、^bと等価) だがそれはキャレット左移動に使われるべきなので後述のように変更する。

key command description
? list-keys
: command-prompt
d detach-client
c new-window
n next-window
p previous-window
l last-window
, rename-window
. move-window
0 1 2 3 select-window -t :=N
" split-window 横長・縦並びに分割: 日
% split-window -h 縦長・横並びに分割: Φ
; last-pane 直前のペイン(往復)
o select-pane -t:.+ 番号順にペインを巡回
select-pane -U
C-↑C-↓ resize-pane -U ペインサイズ変更
C-o rotate-window レイアウトを維持してペインを回す
space next-layout レイアウトを変更する
! break-pane ペインを独立したウィンドウにする
[ copy-mode
] paste-buffer

コピーモード

上に戻ってスクロールしたり、その内容をコピーしたいときはコピーモードを使う。 キーボードから prefix[ で入れるほか、 set -g mouse on を設定すれば上スクロールで自然に入れる。

コピーモードでのキー操作はデフォルトだとemacs風で、 環境変数 EDITOR/VISUAL やオプション mode-keys からviに変更できる。 シェルを介さずに直接起動する場合も考えると明示的にオプション設定しておくのが無難。

command vi emacs description
cancel q esc コピーモード終了
begin-selection space C-space 選択開始点をマーク
copy-pipe-and-cancel enter M-w 選択範囲内を copy-command に送って終了

コピーと同時に終了せずモードや選択状態を維持したい場合はキーバインドを copy-pipecopy-pipe-no-clear に変更する。

copy-pipe* の宛先はデフォルトでtmux内のバッファになっており、 ペーストはtmux内で prefix] するしかない。 macOSで次のように設定しておけば、 ⌘commandcと同じところにコピーして、 アプリを超えて⌘commandvできるようになる:

if "command -v pbcopy" "set -s copy-command pbcopy"

設定

設定ファイル: ~/.tmux.conf

https://github.com/heavywatal/dotfiles/blob/master/.tmux.conf

prefix <key>
使えるのは ^h^[ のようなASCIIキャレット記法が存在するもの。 シェルやエディタであまり使わず左手だけで完結できるキーがいい。 tmux の頭文字で覚えやすい C-t がよかったけど fzf と衝突。
同じキーを bind <key> send-prefix に設定しておけば、 2回押しのうち1回分がtmuxを貫通して伝わる。 使う頻度の低いキーとの衝突ならこれで乗り切れる。
aggressive-resize [on | off]
サイズの異なる端末からattachしたときにウィンドウサイズを変更する。
update-environment <variables>
attachするときに環境変数を親プロセスから持ち込んで既存sessionの値を上書きする。
DISPLAYSSH_AUTH_SOCK などがデフォルトで含まれているので、 -a オプションで追加するのが無難。
TERM_PROGRAM は特殊で、指定しても強制的に tmux に上書きされる。 showenv TERM_PROGRAM で上書き前の情報がとれるようにはなるので、 それを使って環境変数を再上書きすることは可能。 ただしattachの度にそこまで実行できないことには注意。 See tmux#3468.

デタッチ後しばらくしてシェルを起動すると残存セッションを忘れがちなので、 以下のようなものを .zshrc とかに書いておけば表示で気付ける。

if [ -n "$TMUX" ]; then
  eval $(tmux showenv TERM_PROGRAM)
else
  tmux has-session >/dev/null 2>&1 && tmux list-sessions
fi

openpbcopy などがうまく働かなくて reattach-to-user-namespace が必要になる問題は既に解消された。

利用例

  1. リモートサーバーに ssh ログインし、 tmux の新しいセッションを開始:
    ssh remote.sample.com
    tmux
    
  2. ウィンドウを左右に分割し prefix%、 右ペインでPythonインタプリタを起動 python:
  3. 左ペインにフォーカスを戻しprefixo、 ファイルを閲覧したり何だり less ~/.tmux.conf
  4. 新しいウィンドウを作って prefixcroot 仕事をしたり何だり su -
  5. ウィンドウを切り替える prefixl, prefixn, prefixp
  6. このセッションをデタッチし prefixd、 ログアウトして家に帰る exit
  7. 家からサーバーに再び ssh ログインして、 さっきの tmux セッションをアタッチして作業を再開:
    ssh remote.sample.com
    tmux attach -d
    
  8. セッション内のすべてのペインで exit して終了。