やりたいこと
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
で。