tmux

nested Tmux

tmuxを入れ子にするとプレフィックスキー操作が煩雑になるのでこれまでつかわないでいたが、1つのリモートサーバーに複数のシェルを立ち上げたいときにいちいちsshログインするのはやっぱメンドクサイ。リモートでtmuxがうごいていればnew-windowやsplit-windowでさっとシェルを立ち上げられるのに。

tmux公式としては内側のtmuxでprefix2を設定するかprefix連打で対応しろってことだとおもっている。

ちょっと調べてみたがnested tmuxのイケてる実装はなさそうで、でもアイデアはこのnested Tmuxからいただいて、ちょっと作ってみた。

bind-key -n F7 run-shell 'NESTED=$(tmux show-option -v @nested); tmux set-option @nested "X$NESTED"; tmux set-option prefix None; tmux set-option status-style bg=red; test "$NESTED" = "" || tmux send-keys F7'
bind-key -n F9 run-shell 'NESTED=$(tmux show-option -v @nested); tmux set-option @nested "${NESTED#X}"; if test "$NESTED" = "X"; then tmux set-option prefix C-l; tmux set-option status-style bg=white; else tmux send-keys F9; fi'
bind-key F7 send-key F7
bind-key F9 send-key F9

set-option -g status-left "[#S]#{?@nested, #{@nested},}"

使い方

  • F7とF9がホットキーになっている。なぜF7/F9なのかというと、HHKBだと押しやすいから。ちなみにHHKBのFnは左Altにしている。
  • F7をおすと外側のtmuxのprefixが無効化されてprefixキーが内側のtmuxに直接届くようになる。
  • F9を押すと外側のtmuxのprefixが有効になる。

問題点

  • prefixは外側と内側で同じになってる想定。ちがってても動くけど。
  • prefix,F7,F9,bg=が決め打ちなのはダサい。
  • F7連打すると最内側のシェルにF7が貫通する。
  • マウスでwindow/paneの移動をされると厄介。

tmux: cut&paste

tmuxのコピペは便利につかえるが、ひと工夫することでより便利に。

bind-key        [       copy-mode
bind-key        C-[     copy-mode
bind-key        C-]     choose-buffer "paste-buffer -p -b %%" 
bind-key        ]       choose-buffer

choose-bufferをつかうことでペーストしたいバッファをインタラクティブに選べて、不要になったバッファを消すこともできる。

paste-buffer -pオプションで(アプリがサポートしていれば)bracketed paste modeをつかって、エディタに直接ペーストできるので、たとえば普通にペーストするとインデントがずれてしまうところ、braceted paste modeをつかうとエディタが(キー入力じゃなくて)ペーストだと認識できるのでインデントがずれないし、ペーストをUNDOできる(キー入力扱いだと一発でUNDOできない)、巨大なテキストを貼り付けた場合には処理時間が短くて済む利点も。

tmux-logo-small

tmux: 画面分割

~/.tmux.confに

bind-key        /       split-window -hv
bind-key        |       split-window -h
bind-key        -       split-window -v
bind-key        _       split-window -vf

って書いておくと、縦分割横分割で、縦ってどっちだっけ?って悩む必要がなくなってよい。

tmux-logo-small

tmux: ドラッグ&ドロップでペイン入れ替え

~/.tmux.confに

bind-key -T root DoubleClick3Pane select-pane -m
bind-key -T root MouseDragEnd3Pane swap-pane -t= \; select-pane -M

というのを追加しておくと、右ボタンをダブルクリックしてドラッグするとペインの入れ替えができて便利。

tmux-logo-small

tmux2.3: pane-border-status

tmux2.3でpane-border-status/pane-border-formatが追加されてpane_titleが表示できるようになった。便利かもしれない。うっとおしいだけかもしれな。

* New option 'pane-border-status' to add text in the pane borders.

設定はこんなかんじで。

tmux set -g pane-border-status bottom
tmux set -g pane-border-format "#{?pane_active,#[reverse],}#{pane_index}:#{pane_title}"

タイトルを設定するエスケープシーケンスを出すと反映される。

printf "\033]2;`hostname`:`/bin/pwd`\033\\"

hoge

tmuxのレイアウト情報をしらべてみた

tmux list-windows ででてくるlayout情報は tmux select-layout で復元できるが、フォーマットがマニュアルには書いてないのでソースコードをみてみた。

% tmux list-windows
0: emacs  (1 panes) [309x91] [layout bd46,309x91,0,0,9] @4
1: ssh  (1 panes) [309x91] [layout bd45,309x91,0,0,8] @3
2: ssh  (18 panes) [309x91] [layout bbb8,309x91,0,0[309x18,0,0{77x18,0,0,10,77x18,78,0,11,76x18,156,0,12,76x18,233,0,13},309x18,0,19{77x18,0,19,14,77x18,78,19,15,76x18,156,19,16,76x18,233,19,17},309x17,0,38{77x17,0,38,18,77x17,78,38,19,76x17,156,38,20,76x17,233,38,21},309x17,0,56{77x17,0,56,22,77x17,78,56,23,76x17,156,56,24,76x17,233,56,25},309x17,0,74{77x17,0,74,26,231x17,78,74,27}]] @5
3: tcsh- (1 panes) [309x91] [layout ded0,309x91,0,0,40] @12
4: tcsh* (1 panes) [309x91] [layout ded4,309x91,0,0,44] @13 (active)
%

最初の4文字は16進数でカンマ以降の内容のチェックサムになっている。チェックサムルーチンは layout-custom.cのlayout_checksum()でローテートしながら文字を足し込んでいくだけの簡単なもの。ちょっと気になるのは、文字をcharで扱っているのでMSBが立っているとcharがsignedかunsignedかで符号拡張でチェックサムが違ってきてマシン間でポータブルじゃないが、そもそもペーンIDが埋め込まれているのでポータブルである必要がなかったし、そもそもasciiしかつかってないのでok。

レイアウト     ::= チェックサム "," レイアウト情報
レイアウト情報 ::= コンテナ | 単体
コンテナ       ::= 横幅 "x" 高さ "," 左上X座標 "," 左上Y座標 "," 内部
単体           ::= 横幅 "x" 高さ "," 左上X座標 "," 左上Y座標 "," PANE_ID
内部           ::= 上下分割 | 左右分割
上下分割       ::= "[" レイアウト情報 "]"
左右分割       ::= "{" レイアウト情報 "}"

ここでの上下分割はtmux split-window -v、左右分割はtmux split-window -h。これもどっちが縦でどっちが横なのかわかりにくいので、日本語なら上下分割・左右分割と呼ぶのがいいとおもうなぁ。

paneをzoomしているとそのpaneがウィンドウを専有するレイアウトが出力されるんだなぁ。でもこのズーム状態をselect-layoutで復元することはできないようだ。なんか謎。

koie

tmux-swap-windows

tmux-swap-windows.sh
tmuxでウィンドウの順番は swap-window をつかえばできるが、もっとvisuallyにやるスクリプト。$HOME/.tmux.confで適当なキーにわりあててつかっている。
bind-key W run "tmux-swap-windows"
シェルスクリプトtmux-swap-windowsを実行すると編集用のウィンドウが1つできて、その中でviが起動してwindowのリストを編集してZZで終了すると、その順序でウィンドウを並べ替える。ウィンドウ名を書き換えると反映される小ネタもある。
koie

tmux: grouped session

grouped sessionをつかうとscreenでのattachとおなじように、カレント・ウィンドウを独立に切り替えらえるようになる。

それでもscreenとはちがって

  • screenは端末ごとにウィンドウのレイアウトを変えられるが、tmuxはpaneの配置は共有される。
  • tmuxのウィンドウサイズは一番小さな端末の画面サイズになる。screenは fit コマンドをつかえば任意の端末の画面サイズに合わせられる。

grouped sessionのつかいかた

まずは端末エミュレータを2枚ひらく。

1枚目でtmuxコマンドを実行してセッションをつくる。ついでに C-b c でウィンドウをもうひとつ作っておく。

tmux lsを実行するとセッションの一覧がでてくる。こんなかんじ。左端がセッション名になる。

% tmux ls
0: 2 windows (created Thu Dec 27 19:00:07 2012) [80x23] (attached)
%

つづけて2枚目で tmux new-session -s hoge -t 0 を実行すると

一見 tmux attach した場合と同じようにみえる。

でも current window を独立に切り替えられるところが違う。

% tmux ls
0: 2 windows (created Thu Dec 27 19:00:07 2012) [80x23] (group 0) (attached)
hoge: 2 windows (created Thu Dec 27 19:02:38 2012) [80x23] (group 0) (attached)
%

2枚目で起動したtmuxセッションを終わるときは tmux kill-session を実行する。

detachしただけだとセッションが残ってしまうことに注意。

あと、tmux set -g aggressive-resize on を設定しておくのが吉。

koie

なぜかmltermの中でtmuxをつかうと暴走するときがある

mltermのなかでtmuxをうごかしていて、ログをがばっと出力するときによく暴走するので調べてみた。

  • truss -pでみてもsyscallは発行していない。
  • gdbでアタッチして関数をみると奥深いところでクルクルしてるっぽい。
  • ソースコードからつくってgdbの中で実行→暴走させてC-cで止めてみると
    • flush_scroll_cache()がml_term_set_modified_lines()を呼び出すときの第3引数がデカい。
      • screen->scroll_cache_boundary_start = 27
      • screen->scroll_cache_boundary_end = 44
      • screen->scroll_cache_rows = 76
      • となってたので 仮引数 end = 4294967264。

対症療法:

diff --git a/xwindow/x_screen.c b/xwindow/x_screen.c
--- a/xwindow/x_screen.c
+++ b/xwindow/x_screen.c
@@ -755,6 +755,7 @@
 			/*
 			 * scrolling upward.
 			 */
+			if (screen->scroll_cache_boundary_end >= screen->scroll_cache_rows)
 			ml_term_set_modified_lines( screen->term , screen->scroll_cache_boundary_start ,
 				screen->scroll_cache_boundary_end - screen->scroll_cache_rows) ;
 		}
@@ -763,6 +764,7 @@
 			/*
 			 * scrolling downward.
 			 */
+			if (screen->scroll_cache_boundary_start >= screen->scroll_cache_rows)
 			ml_term_set_modified_lines( screen->term ,
 				screen->scroll_cache_boundary_start - screen->scroll_cache_rows ,
 				screen->scroll_cache_boundary_end) ;

mltermの新しいのにしたらこのへんのコードがかわってた。でも同じだった。

(gdb) p screen->scroll_cache_boundary_start
$1 = 39
(gdb) p screen->scroll_cache_boundary_end
$2 = 76
(gdb) p screen->scroll_cache_rows
$3 = 0
(gdb) p scroll_cache_rows
$6 = 87

暴走させるコツがわかってきた。

  • こんなスクリプトを用意しておく(ファイル名は仮にhoge.shとして保存しとく)
#!/bin/sh
I=100
while : ; do
    echo $I
    I=$((I+1))
done
  • mltermをたちあげて広くする。全画面とか。手元の環境では 92 rows; 153 columns; になる (stty -a調べ)
  • tmuxをたちあげる。
  • tmux split
  • tmux set sync
  • sh ./hoge.sh
  • 一丁あがり!

mltermの最新版をビルドしようとしたらlinkでエラーになった。FreeBSDの問題なのかmltermの問題なのかわからんがパッチ:

diff --git a/main/dexport.map b/main/dexport.map
--- a/main/dexport.map
+++ b/main/dexport.map
@@ -2,6 +2,9 @@
 global:
        x_imagelib_load_file ;
        x_get_opened_displays ;
+       # KOIEMEMO: /usr/bin/ld: mlterm: local symbol `__progname' in /usr/lib/crt1.o is referenced by DSO
+       __progname ;
+       environ ;
 local:
        * ;
 } ;

[2012-09-18 12:39]


作者さんから直ったとの連絡をいただきました!

http://mlterm.sourceforge.net/mlterm-3.1.3post-fixscroll.patch のパッチを /usr/ports/x11/mlterm/files/patch-mlterm-3.1.3post-fixscroll に置いて make clean && make && make deinstall && make reinstall で入れ替え。

koie

tmuxのautomatic-renameはどうやってプロセスを決めるか?

automatic-rename on で使っているとステータスラインのwindow nameにそのとき実行しているプロセス名が表示されるが、makeなんかしているときは表示が頻繁に変わって目障りでもある。

いったいどんなふうにプロセス名を決めているのかtmux/osdep-freebsd.c (r2849)をよんでみると、

  • ウィンドウのアクティブ・ペーンの端末を対象に
  • 端末のフォアグラウンド・プロセス・グループID(=グループ・リーダー)を調べて(tcgetpgrp)
  • sysctlでプロセスグループに属するプロセスの一覧をとってきて
  • グループ内プロセスの中からいちばんいいプロセスを選択
  • ただしプロセスの制御端末がペーンの端末になっていること

tmux的にいちばんいいプロセスとは、BSD系だとこんなかんじ:

  • SRUN(実行可能) or SIDL(プロセスができたばかり)なのがよい
  • SSTOP(C-zとかデバッガで止められている) or SZOMB(死んでる)なのはだめ
  • CPUを使っている方がよい(psコマンドのcpuで出てくるやつ) (dragonflyにはない)
  • 寝てる時間が多いのはだめ (dragonflyにはない)
  • P_SINTR(寝てるけど割り込み可)なのがよい (openbsdのみ)
  • それでも差がつかなかったら、コマンド名が辞書順で先なのを選ぶ (netbsdにはない)
  • それも同じだったら、pidが大きい方を選ぶ

この大袈裟なロジックのおかげでmakeを走らせていると頻繁にステータスラインが変化するんだな。

osdep-darwin.cだと端末のフォアグラウンド・プロセス・グループ・リーダー p_comm を表示、osdep-linux.cだと端末のフォアグラウンド・プロセス・グループ・リーダーの/proc/.../cmdlineからargv[0]を表示するようだ。これならmakeを実行中はずっと make と表示されるはずだ。BSD系もこのくらいでいいんじゃないか?

koie

記事検索
月別アーカイブ
アクセスカウンター

    タグ絞り込み検索
    ギャラリー
    • ウヰルキンソン・ジン 37°
    • ウヰルキンソン・ジン 37°
    • 今日の練習 2024-06-14
    • 日傘 3本目
    • 日傘 3本目
    • 日傘 3本目
    Amazon
    楽天市場
    adby google
    LINE読者登録QRコード
    LINE読者登録QRコード