世界を動かす技術を、日本語で。

開発ワークフローにおける tmux の置き換え

概要

tmux の長年ユーザーが、セッション永続化やウィンドウ管理の代替を模索した体験談 kitty プロジェクトや作者の議論から、tmuxの複雑性とエコシステムへの影響を再考 shpool などの軽量ツール導入による利便性と課題 SSH 環境下でのウィンドウ管理とセッション自動接続手法 tmuxから脱却した新しいワークフローの実践例

tmuxの課題とkittyプロジェクトからの気づき

  • tmux を7年以上愛用してきたユーザー視点
  • セッション永続化、ウィンドウ分割、プロジェクトごとのウィンドウグループ化、リモートサーバーでの多端末利用など 多機能性 への依存
  • kitty プロジェクトのGitHub Issueでの指摘:
    • マルチプレクサは 不要なオーバーヘッド と複雑性を追加
    • エスケープコードの変換・改変による バグや非互換性 発生
  • 色表示の問題スクロールバックの使いにくさマウス選択の不安定さ などtmux特有の悩み
  • kitty graphics protocol など新機能への対応遅れ
  • 作者Kovid氏の主張:「マルチプレクサはエコシステム全体の進化を阻害」

tmuxの代替手段とその現実

  • セッション永続化 は他の方法でも実現可能
    • ctrl-z + fg、nohup {cmd} &、disownなど
    • これらはtmuxほど 柔軟な再接続 は困難
  • ウィンドウ管理 はローカルではウィンドウマネージャで代用可能
    • ただしSSH先では ウィンドウマネージャの制御が及ばない
  • tmux の代替となり得る軽量ツール
    • dtachabducoshpool
      • UNIX哲学に則った 単機能志向
      • 仮想分割がないため ネイティブスクロールバック が利用可能
      • デーモン化やUNIXソケットでのプロセス管理
      • shpool はdetachコマンドを備え、キーバインド割当も容易
  • nvim 内でのdetach問題や一部バグは存在
    • detachショートカットが nvim に奪われる場合あり
    • shpool はdetachコマンドで回避可能

SSH環境下でのセッション管理とウィンドウ操作

  • ghosttysway+foot などのローカルウィンドウマネージャ活用
  • クライアント+サーバ開発フロー
    • ローカルマシンから Proxmox VM へ常時SSH接続
  • shpool セッションへの自動接続方法
    • ssh_config でRemoteCommandやControlMaster等を設定
    • 各shpoolセッションごとにsshエイリアス作成
      • 例:ssh d.chat、ssh d.dot、ssh d.term
    • autossh で自動再接続も実現
      • 例:autossh -M 0 d.chat

tmuxからの移行後の変化と課題

  • tmux完全卒業 を実現
    • ワークフローの微調整は必要だが、 大幅な不便さはなし
  • ネイティブスクロールバックターミナル通知タイトル表示 などがスムーズに動作
  • shpool の課題
    • 再接続時のターミナル状態復元が不十分(nvim利用時のリサイズ問題)
      • ワークアラウンド例:vim.keymap.set("n", "<leader>l", function() io.stdout:write("\027[?2048h") end, opts)
    • multiplayer非対応 で複数クライアントからのautossh利用時に切断問題
  • 現状の満足度 は高いが、完璧ではない
    • 新たな問題点や改善余地も認識

結論:tmux代替ワークフローへの提案

  • shpool や軽量セッション管理ツール+ウィンドウマネージャの組み合わせで、
    • セッション永続化
    • ウィンドウ管理
    • SSH環境下での柔軟なマルチセッション運用 がほぼ実現可能
  • tmux の複雑性から解放され、
    • シンプルで拡張性の高いワークフロー 構築が可能
  • まだ発展途上の部分もあるが、
    • tmux以外の選択肢 として十分検討に値する
  • あなたのワークフローにも導入する価値ありか、ぜひ検討・共有を推奨

Hackerたちの意見

「tmuxは必要ないかも」と言うのは、「ブラウザのタブは必要ないかも」と同じようなもんだと思ってる。確かに、ターミナルのセッションが1つか2つ、ウェブページが少しだけなら、別に使わなくても大丈夫かもしれない。でも、それ以上になると、デスクトップがウィンドウをたくさん管理できないから、機能を再実装しなきゃいけなくなるんだよね。

最近、ブラウザのタブやtmuxでたくさんのターミナルタブを開くんじゃなくて、自分の好きなウィンドウマネージャーの素晴らしいウィンドウ管理機能を使うように努力してる。もしそれがうまくいかなかったら、もっといいウィンドウマネージャーが必要だっていうサインだと思うし。何百ものタブを開きっぱなしにする代わりに、ブックマークを使うのも悪くないよね。ブックマークバーがあれば、タブバーがなくても全然大丈夫。

MS Windowsは、Alt TabやWin Tabなどの優れたマルチウィンドウ管理があって、他のOSよりも遥かに優れてる。ターミナルはそれぞれ異なるアイコンや背景色を設定して、見分けやすくしてる。オペレーティングシステム(Windows)が重い作業をやってくれるんだ。Macを約5年間使ってたけど、「すべてのウィンドウがAltタブできる」MS Windowsが恋しかった。Macは「すべてのアプリがコマンドタブできて、各アプリには独自のサブウィンドウ管理がある」って感じだね。

でも、最近のターミナルはタブがあるから、もし単に複数のターミナルを開きたいだけなら、他にも選択肢があるよ。例えばVSCodeとかね!

タブ管理は個々のアプリじゃなくて、ウィンドウマネージャーの仕事だって思うようになったよ。俺のウィンドウマネージャーは、ウィンドウをタイルしたり、重なったウィンドウからタブを作ったりできるんだ。タブは同じアプリからでも、違うアプリからでも大丈夫。

「tmuxでTERMを正しく設定しないと、色が正しく表示されない」 これは他のターミナルエミュレーターにも当てはまるし、実際、色だけじゃなくて、機能キーや編集キーも正しく認識されないことがある。REPがうまく機能しないところで使われることもあるし、単純な相対カーソル移動すら間違って行われることがある。TERMとterminfo/termcapに組み込まれたアイデアは、UnixやLinux系のオペレーティングシステムでターミナルデバイスが動作する方法に内在している。異なるターミナルやターミナルエミュレーターが、必ずしも同じプロトコルを話すわけではないという現実も避けられない。TERMを正しく設定するのは、tmux特有の問題じゃないんだ。

でも、著者はそれをtmuxのせいにしてるけど、ちょっと違う気がする。記事の印象が悪くなるよね。スクロールの問題が本当にtmuxのせいかどうかも分からないし。tmuxは代替スクリーンバッファを使っていて、これはsmcup/rmcupのterminfo機能を使って有効化されるんだけど、その意味は「ウィンドウのビューポートを固定する」ってことなんだ。だから、絶対的なカーソル移動の位置が分かる状態になる。こうなってると、ネイティブなスクロールの試みは影響がないはずで、キー入力やスクロールホイールのイベントはtmuxに直接送られるべきなんだよね。なのに、他のターミナルエミュレーターは代替スクリーンでローカルスクロールを許可してるから、smcup/rmcupの意味が崩れちゃって、tmuxでの簡単なスクロールも台無しになってる。

同意するよ。ほとんどのプログラムに当てはまることだし。設定をちゃんとしないと、期待通りに動かないことがあるからね。TMUXの場合は、何が原因でおかしく見えるのかすぐには分からないから、ちょっとイライラすることもある。でも、解決策が何かは分からないな。デフォルトで256色にする?

TERMを正しく設定するのはtmux特有の問題じゃないよ。そうだね、でももう一つのレイヤーを扱う必要があるから面倒なんだ。tmux特有のTERMがあって、それはターミナルエミュレーターのTERMの上に乗っかるべきなんだ。これが僕の言いたかったこと:tmuxを使うときやデバッグするときには、もう一つのレイヤーを意識する必要があるんだ。tmuxのFAQの一番上を見てみて:https://github.com/tmux/tmux/wiki/FAQ > 注意:ほとんどの表示問題は不正なTERMが原因です!問題を報告する前に、tmuxの内外でTERM設定が正しいことを確認してください。 > tmuxの中ではTERMは「screen」、「tmux」またはそれに類似したもの(例えば「tmux-256color」)である必要があります。それ以外のところで問題を報告しても無駄です! > 外では、ターミナルに合わせて設定するべきです。特に、rxvtやその派生版には「rxvt」を使ってください。

つい数週間前にTmuxについて知ったんだけど、面白い機能の一つがスクリプト可能ってこと。特定のペインにキーストロークをプログラム的に送れるんだ。日本のフォーラムに触発されて、これを使ってClaude CodeがインタラクティブなCLIスクリプトと実際にやり取りできるか考えてみた。CCはbashを通じてスクリプトを起動できるけど、そのスクリプトがユーザー入力を待ってると、CCは(簡単には)やり取りできないんだ。結果的に、Tmuxを活用できることが分かった!それで、Claude Codeを使って「Tmux-cli」っていう小さなツールを作ったんだ。これでCC(または他のCLIコーディングエージェント)がTmuxペインを生成して、そこでスクリプトを起動し、実際にやり取りできるようになった。つまり、ターミナル用のPlaywright/Puppeteerみたいな感じ。これを使うには、uv tool install claude-code-toolsでインストールできるよ。これによって面白い可能性が広がる:CCがインタラクティブなCLIスクリプトを自動でテストできるようになって、私が介入してエラーを指摘する必要がなくなる。CLIコーディングエージェントが別のペインからUIを起動して、Puppeteer MCPを使ってブラウザからテストすることもできる。CCがデバッガーを有効にしたCLIスクリプトを起動して、ブレークポイントを設定することもできるし、トークン効率の良いコード理解、デバッグ、説明ができる。CLIコーディングエージェントが同じまたは他のCLIコーディングエージェントの別インスタンスを生成して操作することもできる。これは「生成して放置」するCCサブエージェントよりもずっと良いよね。話題に上がったTmuxの代替品が、こういうツールを作るのを可能にするのか気になるな。

そうそう、これを使ってもう一つ楽しいことができる:Claude CodeにGemini CLIやOpenCode、他のCCインスタンスとインタラクティブモードで話させたり、コントロールさせたりすること!別のサブエージェントのバリエーションだね。 :)

科学者たちは、自分たちができるかどうかに夢中になりすぎて、すべきかどうかを考えるのを忘れてしまった。

つまり、端末用のPlaywright/Puppeteerみたいなもんだよね。ttyはただのファイルディスクリプタだし…80年代からscript(1)、expect(1)、chat(8)があったから、tmuxは本当に必要ないかも。

tmux-cliラッパーを使うと結果が良くなると思う?僕はClaudeに既存のtmux CLIを使ってsend-keysやcapture-paneをするように言ってるけど、完璧に動いてるよ。ほんとに「Bashツールは使わないで、すべてのコマンドをtmuxセッションで実行する」ってだけで、あとは勝手にやってくれる。

そして、面白い機能の一つはスクリプト化できることだね。つまり、特定のペインにキーストロークをプログラム的に送信できるってこと。興味がある人のために言うと、screenでも「stuff」コマンドを使えばこれができるよ(「何かを箱に詰める」っていう動詞として読んでね)。

もしターミナルで似たようなことをやりたいなら、Kittyのリモートコントロール機能が結構クールだよ: https://sw.kovidgoyal.net/kitty/remote-control/

要約すると:マルチプレクサは不必要なオーバーヘッドを追加し、複雑さのカスケードに悩まされる。なぜなら、実際にエスケープコードを翻訳し、ウィンドウやセッションの概念に合わせてハッキング的に修正しなきゃいけないから。これは私にとっては一つの機能だ。アプリケーションがtermcapをサポートすることが少なくなってきているから、そうすることでいくつかのアプリケーションが私のVT520でまだ動くことができる。正直、kittyの開発者がどう思ってるかなんて気にしないよ。彼には彼の意見があるけど、私にとってtmuxはずっと重要なんだ。それに、alacrittyの方がいいと思う(でも普段はKonsoleを使ってる)。ユーザーとしては、自分にとってうまく機能するものだけが大事で、建築的に最も優雅な解決策が何かなんてどうでもいい。

VT520を持ってるなんて羨ましい!俺は何年も525を探してるけど、すごく高いし、売ってる人が同じ大陸にいないんだ(多分、俺の国のLKのバージョンも持ってないだろうし)。本物のVT525がちゃんと動くかどうか見たくて探してるんだ。* https://jdebp.uk/Softwares/nosh/guide/commands/console-termi... もしtmuxが無くなったら、AIXTermの16色や代替スクリーンバッファ、マウスシーケンスなど、現代の機能を期待するアプリケーションの端末出力を音訳する別の選択肢があるよ。(-:

僕のVT420はハードウェアフロー制御には対応してないけど、ソフトウェアフロー制御は多くの現代のターミナルアプリと相性が悪いんだ。GNU Screenは非常に効果的なワークアラウンドを提供してるし、他の機能もあるからね。現代のターミナルエミュレーターでtmuxをたくさん使ってきたけど、ビンテージターミナルにはこの重要な機能が欠けてるみたい。tmuxがtermcapやterminfoに対応してない壊れたアプリの問題を解決してくれるのは嬉しいけど。

これはLinuxデスクトップ派向けに書かれてるね。彼らにはいいことだけど、tmuxはMacBookでiTerm2を使ってる人には特におすすめ。tmuxの統合がめちゃくちゃ良くて、作業フローに自然に溶け込んじゃうんだ。これを~/.ssh/configに入れておけば、コンピュータを起こしたり接続を変えたりするたびに、ssh tmuxって打つだけでリモートの開発環境に戻れるよ。 Host tmux HostName 1.2.3.4 IdentityFile ~/.ssh/etc.etc.etc RequestTTY force RemoteCommand tmux -CC new -A -s 0 iTerm2のtmux統合を有効にすると、新しいウィンドウが開いて、リモートのtmuxタブやスクロールバッファが、まるでネイティブのiTerm2タブやスクロールバッファのように見えて動くんだ。tmuxのコマンドなんて全然知らないけどね。

わお、tmuxを何回も試してみたけど、全然好きになれなかった。結局screenに戻っちゃって、「もう二度と試さない!」って自分に誓ったのに、また挑戦することにするよ。

俺はこれにmoshとscreenの組み合わせを使ってる。再起動後にセッションを取り戻すために何かを打つだけで済むんだ。ネットワークを変えたり、ノートパソコンを数日間スリープさせてもセッションが切れないよ。 https://www.grepular.com/Immortal_SSH_Sessions

それなら、毎回打つ手間を省くために、エイリアスを追加するといいよ。 alias looptmux='while true; do ssh tmux; sleep 2; done'

どのLinuxターミナルも同じような統合があるのかな?今はGNU Screenを使ってるけど、tmuxをもう一度試してみようと思ってる。

vimやtmux、iTerm2でちょっとした色やフォントの問題が多すぎて、tmuxを諦めちゃった(ローカル作業用)。ローカルマシンでtmuxから得た小さな利点(基本的にはアップデートに耐えたり、セッションの持続性が少し増えたり)は、ほとんど気にならない。もっと良くなってほしかったけど、フォントの問題が解決できれば戻るかもしれない。でも、今は時間がないんだよね。

moshを使えば、少なくともスリープから復帰した後にSSHを再接続する手間が省けるんじゃない?再起動後はmosh接続を再確立する必要があるけど。

すごい、Ghosttyみたいなのがこれを実装できるか考えてたけど、もう実証済みなんだね。すべてがまだtmuxを「通って」るの?(つまり、パースとかがまだ二回やってるの?)それとも、iTermがほとんどのレンダリングを処理して、スクロールバックの保存やセッションの持続だけをtmuxに任せてるの?後者の方が両方の良いとこ取りみたいだね。

俺はまだtmuxを使い続けるつもり。複数のセッションを管理するのが好きで、プロジェクト間の切り替えが簡単だし、再起動後に復活させるのも楽なんだ。tmux-yankを使ったマウスのコピー&ペーストにも問題はなかったし、これを何年も使ってる。tmuxのクールな機能の一つは、キーを送信できること。数ヶ月前にdotfilesを見直してた時にそれをやったんだ。エイリアスや他のシェルファイルを何度も変更して、何十個ものペインでそれらのファイルをソースしたり、特定の設定ファイルを変更した時にneovimをリロードしたりしたかったから。これをtmuxの組み込みコマンドとシェルスクリプトの組み合わせで簡単に実現できたよ。これについてはここに投稿と動画を作ったんだ。 https://nickjanetakis.com/blog/running-commands-in-all-tmux-...

別の例としてバッファのスクロールバックがあるね。ウィンドウをスクロールするtmuxのやり方を学ぶ必要があるんだ。慣れるけど、あんまり良くないよね。それに、マウスで選択してコピー/ペーストするのはどうなの?大体はうまくいくけど、時々tmuxが無視されて、分割されたところを選択しちゃって、コピーするのが難しくなるんだ。面白いことに、そういうことがあるからtmuxを使ってる!僕の2台目のノートパソコンはDebianのターミナル専用の古いやつで、マウスが使えないから、コピー&ペーストする唯一の方法はtmuxなんだ(多分screenでもできるけど、使い方を知らない)。僕にとってtmuxは代替不可能だね。

Ghosttyのペインやタブが使えるなら、tmuxをローカルでやめようかとも思ったけど、やっぱりtmuxのシングルハイトのステータスバーが好きなんだよね。ウィンドウリストだけのやつ(set -g status-left '' + set -g status-right '')の方が、macos-titlebar-style = tabsの厚いウィンドウデコレーションよりもいいかな。もし誰かの役に立てばと思って、Ghosttyのバインディングを作ったよ(俺のtmuxリーダーはctrl + space):

デフォルトのバインディングをクリア + ペーストのキーを追加

keybind=clear keybind=super+v=paste_from_clipboard

ペインを移動

keybind=ctrl+h=goto_split:left keybind=ctrl+j=goto_split:bottom keybind=ctrl+k=goto_split:top keybind=ctrl+l=goto_split:right keybind=ctrl+space>shift+apostrophe=new_split:down keybind=ctrl+space>shift+five=new_split:right keybind=ctrl+space>space=equalize_splits keybind=ctrl+space>z=toggle_split_zoom

タブを移動

keybind=ctrl+space>c=new_tab keybind=ctrl+space>one=goto_tab:1 ... keybind=ctrl+space>zero=goto_tab:10

いいね、Ghosttyを試してるときに似たようなことを考えたよ。リサイズのためにキーを連続で押し続ける設定はできた?俺のtmuxでは、リーダーキーを押してShiftを押しながらh/i/k/jを連続で押すと、例えばShift+hを押し続けている間、ペインがリサイズされるんだ。でも、Ghosttyではそれを再現できなかったんだよね。

このブログ記事を見て、tmuxを使う理由を思い出したよ。tmuxのワークフローに似せるためにどれだけのことをしなきゃいけなかったか見た?マジで、tmux使った方がいいよ。たまに変なコピー&ペーストに対処するのは気にしないし。

要するに、マルチプレクサは不要なオーバーヘッドを追加して、複雑さのカスケードに苦しむ。なぜなら、実際にエスケープコードを翻訳しなきゃいけないからで、ウィンドウやセッションの概念に合わせるためにハック的に修正する必要がある。 それがtmuxを使うことと何の関係があるの?君はtmuxのコードベースを維持してるわけじゃないでしょ。

たまに変なコピー&ペーストに対処するのは気にしないし。 この問題はtmuxだけのものじゃないよ。描画を引き継ぐどんなターミナルアプリでも同じ問題がある、例えばvimとかね。とはいえ、解決するのは簡単だよ。解決策はOSC52、エミュレーターがシステムクリップボードとやり取りするために使えるターミナルエスケープシーケンス(kitty、alacritty、iterm2はこれをサポートしてる)。最初のステップは、そのプロトコルでデータを書き出すスクリプトを手に入れること。簡単だよ:

#!/usr/bin/env python3
import os, base64, sys
clip = base64.b64encode(sys.stdin.buffer.read())
for pid in (os.getpid(), os.getppid()):  # osc52-copyがttyを持たないプロセスから呼ばれるため
    cty = f"/proc/{pid}/fd/1"
    try:
        fd = os.open(cty, os.O_WRONLY)
        if os.isatty(fd):
            os.write(fd, b'\x1b]52;c;')  # 実際のエスケープシーケンス
            os.write(fd, clip)
            os.write(fd, b'\a')
            break
    except:
        continue
    finally:
        os.close(fd)
else:
    raise SystemExit(f"no tty")

これでこうできる:

$ grep my_thing < some.txt | osc52-copy

osc52に入ったものは、今やシステムクリップボードにあるよ。tmux(set -g clipboard on)とnvim(unset clipboard)はどちらもOSC52をネイティブにサポートしてるし、上のスクリプトを使って他の場所にも統合できるよ。

突然問題を作り出す人を見るのはもううんざりだ。著者が挙げた理由はバカらしいし、彼の「7年以上」のtmux使用について疑問を持つつもある。

さらに、tmuxがうまく処理できなかったことに気づき始めてるけど、今は「普通に動く」ようになったよ。ネイティブのスクロールバック、ターミナル通知、ターミナルタイトルが特に目立つ変更点かな。tmuxのOSウィンドウタイトルと内部ウィンドウタイトルを好きなように設定できるよ。「set -g set-titles」を~/.tmux.confに追加すればOK。設定を変えずにテストしたいなら、^B : set -g set-titlesって入力して、デフォルトに戻したいときは^B : set -gu set-titlesを使ってね。これは便利だけど、ちょっと冗長かも。さらに設定をカスタマイズできるよ。例えば、set -g set-titles-string "tmux | #{pane_title}"ってやると、シェルで設定したタイトルが入るようになる(PS1では、現在のディレクトリを\w\Wで設定してる)。それから、tmuxのウィンドウタイトル(「ウィンドウリスト」に表示されるやつ)も現在のディレクトリ名にしたいなら、set -g automatic-rename-format "#{pane_title}"を使えばいいよ。