WADA-DEV(7) $ /ja/blog/tmux-nested-passthrough-toggle/

NAME

tmux-nested-passthrough-toggle — 自動tmuxに慣れた指のまま、SSH先のtmuxも触りたい ─ ネストをprefix+Tで黙らせるまで

SYNOPSIS

ターミナルでtmuxを自動起動する運用に慣れると、SSH先でtmuxを使った瞬間にネストして内側にキーが届かなくなる。定番のF12トグルは効くが押しづらいので、prefix+Tで外側を黙らせて内側に素通りさせる形に落ち着いた話。ローカルはネイティブ分割という主流解をAlacritty併用で捨てた判断、外側status barを隠す仕上げ、ControlMaster併用とNixOS鯖での/bin/bashハードコードの罠まで。

DESCRIPTION

ターミナルを開いたら勝手にtmuxに入る、という運用に慣れてしまった。Ghosttyなら設定一行だ。

command = tmux new-session -A -s 0

セッションが常に1つあって、ウィンドウを閉じても生きている。快適。ただこれに慣れると、SSH先でもtmuxを使った瞬間に地獄が始まる。

症状:内側にキーが届かない

リモートのGPU鯖で腰を据えて作業するとき、当然あっちでもtmuxを立てたい。切断耐性が欲しいからだ。で ssh -t gpubox 'tmux new -A -s main' すると、ローカル(外側)tmuxと、SSH先(内側)tmuxが入れ子になる。

問題は両方がprefix C-b を待ち受けていること。内側でペイン分割しようと C-b % を押しても、外側が先に食うので内側には届かない。内側のtmuxはほぼ操作不能になる。

他の人はどうしてるのか

調べると、はっきり2派に分かれていた。

派閥1:ローカルはターミナルのネイティブ分割、tmuxはリモート専用。 ローカルでtmuxを使わなければそもそもネストしない。GhosttyやWezTermのネイティブ分割はprefix不要でクリップボードも共有できる。「ローカルの分割はあくまで画面上の手軽さ、tmuxは作業を支える土台」という住み分けだ。

派閥2:ローカル自動tmuxを維持して、ネストを飼い慣らす。 F12トグルやデュアルprefixで対処する。

最初は派閥1に惹かれた。でも自分はGhosttyだけでなく別マシンでAlacrittyも使う。Alacrittyは設計思想として分割もタブも持たない(multiplexingはtmuxに委ねる、が公式の立場)。つまり派閥1だとAlacritty機だけ運用が崩れて、指が混乱する。

全機で指の慣れを揃えたいなら、答えは逆に明確になった。全機でローカルtmuxを使い、ネストだけ飼い慣らす。dotfilesは一つ書けば全機に配られるんだから。

F12は押しづらい

派閥2の定番はSamoshkinのF12トグルだ。F12を押すと外側tmuxがprefixとキーテーブルを丸ごと「お休み」にして、全キーが内側に素通りする。もう一度押すと戻る。prefix不要の単独キーで往復できるのが売り。

書いて試したら、ちゃんと動く。動くのだが、F12が遠い。ファンクション列まで手を伸ばして、しかも頻繁にトグルするキーがそこにあるのは指に優しくない。普段の指の位置で打ちたい。

prefixはどうせ常に指の下にある(C-b)。だったらトグルもprefix経由に寄せればいい。

prefix+Tに落ち着いた

最終形はこう。C-b T で外側を黙らせ、素の T で戻る。

# C-b T で外側tmuxを黙らせ、全キーを内側(SSH先)へ素通り
bind T \
set prefix None \;\
set key-table off \;\
set status off \;\
refresh-client -S
# 黙らせている間はprefixが無効なので、戻りキーはoffテーブルに直キーで置く
bind -T off T \
set -u prefix \;\
set -u key-table \;\
set -u status \;\
refresh-client -S

ハマりどころが一つ。「入る」と「戻る」を同じ操作にはできない。 外側を黙らせた後はprefix(C-b)が無効になっているので、prefix+キーの入口がもう押せない。だから戻りは bind -T off で、prefix無しの素の T に割り当てる。off キーテーブルにいる間はこれが拾われる。

F12が単独キーで往復トグルにできたのは「prefix無しだから黙らせても押せる」からで、prefixに寄せた瞬間に入口と出口を分ける必要が出てくる、というわけだ。

「今どっちにいるか」を可視化する

最初は外側オフ中に赤い●をステータス左に出していた。でもよく考えたら、もっと素直な手がある。外側のステータスバーごと消せばいいset status off)。

そうすると外側オフの間は外側のバーが消えて、内側tmuxのバーだけが見える。二重バーも解消されるし、「今は内側を操作している」が一目で分かる。戻れば外側バーが復活する。上のconfigが既にこれになっている。

テーマがcatppuccinなら、色を直書きせず @thm_red のようなテーマ変数を参照しておくと、flavorを変えても勝手に追従する。バナーを出す方式にするならこれが効く。

合わせ技:再ログインとポータビリティ

ネストを解いたうえで、二つ仕込んでおくと快適だった。

ペイン分割ごとの再ログインを消す。 そもそもの不満は「ローカルtmuxでペインを割るたびにSSHし直すのが遅い」だった。SSHのControlMasterで一本の接続を使い回せば、新ペインのsshが一瞬で繋がる。

Host gpubox
HostName 192.168.1.50
User myuser
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m

一本目が master を張り、二本目以降は認証もハンドシェイクも無しで相乗りする。手元では二本目が0.03秒で繋がった。

configをポータブルにする。 自分の鯖はNixOSなのだが、tmux.confに default-shell /bin/bash と書いていたら鯖側で壊れた。NixOSには /bin/bash が存在しない(/bin/sh はあるが)。ハードコードをやめてPATH経由の bash にすれば、ArchでもNixOSでも動く。

# /bin/bash 決め打ちをやめる。PATHのbashなら両対応
set-option -g default-command 'bash -i'

まとめ

  • ローカル自動tmux × リモートtmuxはネストする。外側がprefixを食って内側に届かない。
  • ローカルをネイティブ分割に寄せる主流解もあるが、分割を持たないAlacrittyを併用するなら全機tmuxで揃える方が指が楽。
  • C-b T で外側を黙らせ、素の T で戻る。F12でも動くが、頻繁に押すなら指の届くprefix側が楽。入口と出口でキーの置き場所(prefixテーブル vs off テーブル)が変わるのがミソ。
  • 外側のステータスバーを消すと「今は内側」が可視化される。
  • ControlMasterで再ログインを消し、/bin/bashハードコードをやめてポータブルにすると、全マシンで同じ体験になる。

この記事の tmux.conf は dotfilesリポジトリ に置いてある(SSH configは実ホスト名が入るので置いていない)。

COMMENTS