やりたいこと
GNU screenでは各端末部分をWindowと呼び、
それらはscreenのセッション全体で管理されていて
分割表示しているような場合には管理しているWindowの中から
適宜選んで表示することが出来ます。
一方、tmuxでは各端末部分はPaneと呼ばれ、
WindowはPaneをまとめた表示状態を指します。
各Paneは何れかのWindowに属していて、
そのWindowで必ず表示される様になっています。
tmuxに慣れてみる: tmuxとGNU screenの違いなど
screenの場合は複数端末を立ち上げて全体で管理してその時その時で
表示したいものだけを表示する、と言った感じ。
例えば分割表示してある中の1つのWindowだけ隠れてるWindowと交換、
みたいなことが簡単に出来ます。
一方、tmuxの場合は各Windowで必要な端末群を必要に応じて表示しておいて
Window毎切り替えてそれぞれで使う、と言った感じ。(個人的感想。)
こちらは現在表示されてるWindowの中の1つのPaneだけ他のWindowの
物と交換しようとするとswap-paneというコマンドがありますが、
現在表示されてないものが複数ある場合に順番に交換していく、みたいなことを
するにはちょっと工夫が必要だったりします。
個人的にはscreenの様な使い方をしたいので、
tmuxの方で、
- 最初のWindowsをscreenの通常表示画面
- 2番めのWindowを、screenでの隠れてるWindowの管理場所
みたいな風にしてみたいと思います。
スクリプトの準備
tmux内のコマンドだけだと難しい点もあるので、
外部コマンドを使ってPaneの整理やステータス表示等を行います。
こんな感じのスクリプトを作ってPATHが通ってる所に置いておきます。
tmuxUtils
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
| #!/usr/bin/env bash
function tmuxStatus {
#printf "\e]2;$(hostname -s)\e\\"
printf "\e]2;myhost\e\\"
}
function tmuxWindowName {
IFS_ORIG=$IFS
IFS=$'\n'
wins=($(tmux list-windows -F "#I #{window_active}"))
IFS=$IFS_ORIG
current=0
noview=0
for line in "${wins[@]}";do
iw=$(printf "$line"|cut -d' ' -f1)
fw=$(printf "$line"|cut -d' ' -f2)
if [ "$fw" -eq 1 ];then
current=1
else
current=0
fi
IFS_ORIG=$IFS
IFS=$'\n'
panes=($(tmux list-panes -t $iw -F "#D #{pane_active} #{pane_current_path}"))
IFS=$IFS_ORIG
first=1
status=""
for line in "${panes[@]}";do
ip=$(echo "$line"|cut -d' ' -f1|sed 's/%//')
fp=$(echo "$line"|cut -d' ' -f2)
pp=$(echo "$line"|cut -d' ' -f3)
if [ $first -eq 1 ];then
first=0
else
status="$status#[bg=colour238] #[bg=colour008]"
fi
if [ $current -eq 1 ] && [ $fp -eq 1 ];then
status="$status#[bg=colour255]"
else
status="$status#[bg=colour008]"
fi
status="$status${ip}:$(hostname -s) $(basename $pp)"
done
status="$status#[bg=colour236] #[bg=colour008]"
tmux rename-window -t :$iw "${status}"
done
}
function tmuxLeft {
IFS_ORIG=$IFS
IFS=$'\n'
wins=($(tmux list-windows -F "#I #{window_active}"))
IFS=$IFS_ORIG
current=0
status=""
noview=0
for line in "${wins[@]}";do
iw=$(printf "$line"|cut -d' ' -f1)
fw=$(printf "$line"|cut -d' ' -f2)
if [ "$fw" -eq 1 ];then
current=1
else
current=0
fi
IFS_ORIG=$IFS
IFS=$'\n'
panes=($(tmux list-panes -t $iw -F "#D #{pane_active} #{pane_current_path} #T"))
IFS=$IFS_ORIG
first=1
for line in "${panes[@]}";do
ip=$(echo "$line"|cut -d' ' -f1|sed 's/%//')
fp=$(echo "$line"|cut -d' ' -f2)
pp=$(echo "$line"|cut -d' ' -f3)
tp=$(echo "$line"|cut -d' ' -f4)
if [ $first -eq 1 ];then
first=0
else
status="$status#[bg=colour238] #[bg=colour008]"
noview=$((noview+30))
fi
if [ $current -eq 1 ] && [ $fp -eq 1 ];then
status="$status#[bg=colour255]"
noview=$((noview+15))
else
status="$status#[bg=colour008]"
noview=$((noview+15))
fi
status="$status${ip}@${tp} $(basename $pp)"
#status="$status${iw}.${ip}:#h $(basename $pp)"
#noview=$((noview-15))
done
status="$status#[bg=colour000]..#[bg=colour008]"
noview=$((noview+30))
done
echo -n ${status: 0: $(($(tput cols)+noview-32))}
}
function tmuxAlign {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -lt 2 ];then
return
fi
flag=$(tmux list-windows -F "#{window_active} #I"|grep ^1|sed 's/^1 //')
if [ "$flag" -ne 0 ];then
tmux swap-window -t:+
fi
for((i=2;i<${#wins[@]};i++));do
panes=($(tmux list-panes -t ${wins[$i]} -F "#P"))
for((j=0;j<${#panes[@]};j++));do
tmux join-pane -d -s :${wins[$i]}.${panes[$j]} -t :${wins[1]}.bottom
done
done
}
function tmuxCreate {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -eq 1 ];then
tmux new-window -d
tmux swap-pane -d -s :+
else
tmux split-window -d -t :+.bottom
tmux swap-pane -s :+.bottom
fi
tmuxAlign
}
function tmuxSplit {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -eq 1 ];then
tmux split-window -v
else
tmux join-pane -v -s :+.top
fi
tmuxAlign
}
function tmuxVSplit {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -eq 1 ];then
tmux split-window -h
else
tmux join-pane -h -s :+.top
fi
tmuxAlign
}
function tmuxOnly {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -eq 1 ];then
tmux break-pane
else
IFS_ORIG=$IFS
IFS=$'\n'
panes=($(tmux list-panes -F "#D #{pane_active}"))
IFS=$IFS_ORIG
for line in "${panes[@]}";do
ip=$(echo "$line"|cut -d' ' -f1)
fp=$(echo "$line"|cut -d' ' -f2)
if [ $fp -ne 1 ];then
tmux move-pane -d -s ${ip} -t :+
fi
done
fi
tmuxAlign
}
function tmuxMove {
wins=($(tmux list-windows -F "#I"))
if [ ${#wins[@]} -eq 1 ];then
tmux break-pane
tmux select-window -t:+
else
IFS_ORIG=$IFS
IFS=$'\n'
panes=($(tmux list-panes -F "#D #{pane_active}"))
IFS=$IFS_ORIG
for line in "${panes[@]}";do
ip=$(echo "$line"|cut -d' ' -f1)
fp=$(echo "$line"|cut -d' ' -f2)
if [ $fp -eq 1 ];then
tmux move-pane -d -s ${ip} -t :+
fi
done
fi
tmuxAlign
}
function tmuxNext {
tmux swap-pane -s :+.top
tmux rotate-window -U -t :+
paneinfo=$(tmux list-panes -F "#{pane_active} #D@#T #{pane_current_path}"|grep ^1|sed 's/^1 %//')
tmux display-message "$paneinfo"
}
function tmuxPrev {
tmux swap-pane -s :+.bottom
tmux rotate-window -D -t :+
paneinfo=$(tmux list-panes -F "#{pane_active} #D@#T #{pane_current_path}"|grep ^1|sed 's/^1 %//')
tmux display-message "$paneinfo"
}
function tmuxClipborad {
if [ "$CLX" = "" ];then
if [[ "$OSTYPE" =~ linux ]];then
if which -s xsel;then
CLX="xsel"
elif which -s xsel;then
CLX="xclip"
fi
elif [[ "$OSTYPE" =~ cygwin ]];then
if which -s putclip;then
CLX="putclip"
elif which -s xsel;then
CLX="xsel"
elif which -s xsel;then
CLX="xclip"
fi
elif [[ "$OSTYPE" =~ darwin ]];then
if which -s pbcopy;then
CLX="pbcopy"
fi
fi
fi
if [ "$CLX" != "" ];then
tmux save-buffer -| $CLX
tmux display-message "clipboard: $(tmux save-buffer -)"
else
tmux display-message "No clipboard software is found."
fi
}
if [ "$1" = "status" ];then
tmuxStatus
elif [ "$1" = "winname" ];then
tmuxWindowName
elif [ "$1" = "left" ];then
tmuxLeft
elif [ "$1" = "create" ];then
tmuxCreate
elif [ "$1" = "align" ];then
tmuxAlign
elif [ "$1" = "split" ];then
tmuxSplit
elif [ "$1" = "vsplit" ];then
tmuxVSplit
elif [ "$1" = "only" ];then
tmuxOnly
elif [ "$1" = "move" ];then
tmuxMove
elif [ "$1" = "next" ];then
tmuxNext
elif [ "$1" = "prev" ];then
tmuxPrev
elif [ "$1" = "clip" ];then
tmuxClipborad
fi
|
scripts/tmuxUtils
各関数は
- tmuxSatus (status): 現在のPaneのタイトルを変更する(ホスト名にする)。
- tmuxWindowName (winname): 現在のWindowの名前にPane情報等を表示。
- tmuxLeft (left): 全Window全Paneの情報表示。(status-leftで使うつもりなので
left
にしてあります。)
- tmuxCreate (create): 今いるPaneを次のWindowに移し、今いる領域に新しいPaneを作成して表示。
- tmuxAlign (align): 3つ以上Windowがあったりする場合、2つ目以降のWindowを1つにまとめる。
- tmuxSplit (split): 横分割して、もし他のWindowにPaneがあればそれを持ってくる、無ければ新しいPaneを作成。
- tmuxVSplit (vsplit): 縦分割して、もし他のWindowにPaneがあればそれを持ってくる、無ければ新しいPaneを作成。
- tmuxOnly (only): 今いるPaneだけのWindowを作成し、他のPaneを1つのWindowにまとめる。
- tmuxMove (move): 今いるPaneを次のWindowに移動させる。
- tmuxNext (next): 今いるPaneを次のWindowの最初のPaneと交換。
- tmuxPrev (prev): 今いるPaneを次のWindowの最後のPaneと交換。
- tmuxClipborad (clip): tmuxのクリップボードをOSのクリップボードへ渡す。
となります。
最初のPaneタイトルをホスト名にするところですが、これはsshの接続先でも
この設定を入れておくことでタイトルに現在居るホスト名を出すことが可能になります。
tmuxWindowName
は使ってませんが、最初、
ステータス表示をWindow Nameを使って表示しようとした時に作ってみた物。
今はtmuxLeft
の方を使って左領域にコマンド結果を出力するようにしています。
ステータス表示は、Window Nameの方はWindowの情報が変わる度に変更され、
status-left
に指定する左側領域は
status-interval
で指定する時間(秒数)で変更されます。
上の様な設定でPROMPT_COMMAND等にtmuxUtils winname
を指定して毎回実行させる、
みたいなことをしてみましたが、
表示が変更されるタイミングはWindowが変更された直後なので
これだと変更直後には上手く反映されません。
Window Nameでやりたい場合にはWindowの変更を行う前に名前を予め変えておいてから
Windowを操作、みたいな事をしないといけません。
さらに内部でもともとあるコマンドとかも全部ラップかけないと上手くいかないので余り
上手くないので辞めました。
一方でstatus-left
の方は最短1秒毎で自動アップデートされるので、
一瞬タイムラグがありますが、Window Nameの場合と違って変更後必ず
反映はされるのでこちらにしました。
また、Window Nameは表示幅に比べて長すぎる場合、適当に余分な部分をカットして
表示してくれますが、
status-left
に長いものを入れると何も表示されなくなります。
そのため、最後に
echo -n ${status: 0: $(($(tput cols)+noview-32))}
と、適当に表示部分を短くしてますが、#[bg=colour008]
等の部分は
実際には表示されないので差っ引いて考える必要があるので、
その分余分に表示するようnoview
という変数を入れています。
最後に32文字分引いてますが、この辺イマイチ表示文字と計算と合わないんですが、
このくらい引いておくと上手く表示されたので取り敢えずの数字。
また、tmuxに慣れてみる: tmuxとGNU screenの違いなど
にも書いたとおり、tmuxではscreenやVimと縦分割、横分割の意味合いが逆なので、
上で言っている横分割
はscreenやvimでの意味です。
(なのでコマンドは横分割の方が-v
で縦分割の方が-h
になっています。)
最後のはPane操作とは関係ありませんが、クリップボード渡し用の関数です。
.bashrc
ステータスを更新するため、
.bashrc
1
2
3
4
5
| if [ "$TERM" = screen ]; then
if [ "$TMUX" != "" ];then
PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}tmuxUtils status"
fi
fi
|
.tmux.conf
.tmux.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| # Split
#unbind-key '"'
#unbind-key %
bind-key s run 'tmuxUtils split'
bind-key S run 'tmuxUtils split'
bind-key C-s run 'tmuxUtils split'
bind-key v run 'tmuxUtils vsplit'
bind-key V run 'tmuxUtils vsplit'
bind-key C-v run 'tmuxUtils vsplit'
# window/pane
bind-key n run 'tmuxUtils next'
bind-key C-n run 'tmuxUtils next'
bind-key p run 'tmuxUtils prev'
bind-key C-p run 'tmuxUtils prev'
# create new window
bind-key c run 'tmuxUtils create'
bind-key C-c run 'tmuxUtils create'
# Only one pane, and reverse
bind-key q run 'tmuxUtils only'
# Move current pane to back
bind-key m run 'tmuxUtils move'
# Same size windows
bind-key r select-layout tiled \; run 'tmuxUtils align'
bind-key C-r select-layout tiled \; run 'tmuxUtils align'
# Change pane
bind-key h select-pane -L
bind-key j select-pane -D
bind-key k select-pane -U
bind-key l select-pane -R
bind-key -r C-h select-pane -L
bind-key -r C-j select-pane -D
bind-key -r C-k select-pane -U
bind-key -r C-l select-pane -R
# Resize pane
bind-key -r C-y resize-pane -L
bind-key -r C-u resize-pane -D
bind-key -r C-i resize-pane -U
bind-key -r C-o resize-pane -R
# Clipboard
bind-key a choose-buffer
bind-key C-a choose-buffer
bind-key A run "tmuxUtils clip"
# Border
set-option -g pane-border-style "fg=colour008,bg=colour008"
set-option -g pane-active-border-style "fg=white,bg=white"
# Status line
set-option -g status on
set-option -g status-interval 1
set-option -g status-style "bg=colour236,fg=black"
set-option -g status-left "#(tmuxUtils left)"
set-option -g status-left-length 1000
set-option -g status-right ""
set-window-option -g window-status-format ""
set-window-option -g window-status-current-format ""
set-window-option -g message-style "bg=colour255,fg=black"
|
まとめると通常作業では、
- Prefix cで新規Paneを現在居る所に作成。
- Prefix s/vで横分割、縦分割(既に隠れてるPaneがあればそれを持ってくる。なければ新しいPaneを作成して設置。)
- Prefix h/j/k/lで左/下/上/右へPane間移動。
- Prefix n/pで次/前のPaneを現領域に表示。
- Prefix Ctrl-y/u/i/oでPaneの幅を変更。
- Prefix qで今いるPaneだけのWindowに。(Prefix zで一時的に一つだけ表示にして戻せる設定がデフォルトである。一方、この場合はリセットされるので一からレイアウトを作り直したいときに使う。)
- Prefix mで今いるPaneを隠す。(次のWindowに移す。)
- Prefix r等間隔になるようにPaneを並び替える。
Prefix nで今いるPaneと次のWindow(:+
)の最初の
Pane(.top
)と交換して、次のWindowの順序を回します
1。
これで、Prefix nを続けることで次々と
隠れているPaneを選択することが出来
screenとおなじ感覚で使えるようになります。
Prefix pの方は逆回り。
さらに、通常ステータスラインにはカレントディレクトリのディレクトリ名だけ
表示してるので、
選択時には全パス見れる様にdisplay-message
で表示を行っています。
Prefix n/pはデフォルトでWindowの移動に
割り当てられてますが、この設定では基本的にWindowは
最初の表示用Windowと残りのPaneを置いておくための
WindowだけなのでWindowを移動する必要が無いので潰しました。
必要なら:next-window
/previoous-window
で。