Tmux使用手冊

路易斯發表於2017-10-10

本文首發於CSDN網站,下面的版本又經過進一步的修訂。

關於

全文共12k字,閱讀需20分鐘。

導讀

我一直信奉簡潔至上的原則,桌面視窗的數量越少,我的心情就越愉悅,開發的效率也就越高。反之,雜亂的桌面,暴漲的Chrome tab數量,或是無數的終端視窗,它們會逐步侵佔我的注意力,分散我的思維,最終令我難以專注。因此桌面上我很少放檔案,使用Chrome時常點 OneTab 回收標籤頁,切進終端時使用tmux管理視窗。

我很懶,懶到期望開機後不需要任何操作,本地的十幾種web開發伺服器就自動執行,當然我不希望連續彈出十幾個視窗或是tab,我需要的是靜默無感知的啟用服務,然後還能快速地進入到現場進行操作,web伺服器執行時不佔據終端視窗,關閉iTem2後操作現場不會被銷燬。我的這些期望,tmux都能幫我實現,實際上,除了這些,tmux還能做得更多更好。

到目前為止,tmux幫助我兩年有餘,它帶給我許多驚喜。獨樂不如眾樂,願你也能一同享受tmux帶來的快樂。

簡介

tmux是一款優秀的終端複用軟體,它比Screen更加強大,至於如何強大,網上有大量的文章討論了這點,本文不再重複。tmux之所以受人們喜愛,主要得益於以下三處功能:

  • 絲滑分屏(split),雖然iTem2也提供了橫向和豎向分屏功能,但這種分屏功能非常拙劣,完全等同於螢幕新開一個視窗,新開的pane不會自動進入到當前目錄,也沒有記住當前登入狀態。這意味著如果我ssh進入到遠端伺服器時,iTem2新開的pane中,我依然要重新走一遍ssh登入的老路(omg)。tmux就不會這樣,tmux視窗中,新開的pane,預設進入到之前的路徑,如果是ssh連線,登入狀態也依舊保持著,如此一來,我就可以隨意的增刪pane,這種靈活性,好處不言而喻。
  • 保護現場(attach),即使命令列的工作只進行到一半,關閉終端後還可以重新進入到操作現場,繼續工作。對於ssh遠端連線而言,即使網路不穩定也沒有關係,掉線後重新連線,可以直奔現場,之前執行中的任務,依舊在跑,就好像從來沒有離開過一樣;特別是在遠端伺服器上執行耗時的任務,tmux可以幫你一直保持住會話。如此一來,你就可以隨時隨地放心地進行移動辦公,只要你附近的計算機裝有tmux(沒有你也可以花幾分鐘裝一個),你就能繼續剛才的工作。
  • 會話共享(適用於結對程式設計或遠端教學),將 tmux 會話的地址分享給他人,這樣他們就可以通過 SSH 接入該會話。如果你要給同事演示遠端伺服器的操作,他不必直勾勾地盯著你的螢幕,藉助tmux,他完全可以進入到你的會話,然後靜靜地看著他桌面上你風騷的鍵盤走位,只要他願意,甚至還可以錄個屏。

以上,只是主要功能,更多功能還在後頭,接下來我將詳細地介紹tmux的使用技巧。

安裝

首先安裝之。

在Mac中安裝:

# 先安裝Homebrew,有則跳過
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
# 安裝tmux
brew install tmux複製程式碼

在Linux中安裝:

sudo apt-get install tmux複製程式碼

基本概念

開始之前,我們先了解下基本概念:

tmux採用C/S模型構建,輸入tmux命令就相當於開啟了一個伺服器,此時預設將新建一個會話,然後會話中預設新建一個視窗,視窗中預設新建一個皮膚。會話、視窗、皮膚之間的聯絡如下:

一個tmux session(會話)可以包含多個window(視窗),視窗預設充滿會話介面,因此這些視窗中可以執行相關性不大的任務。

一個window又可以包含多個pane(皮膚),視窗下的皮膚,都處於同一介面下,這些皮膚適合執行相關性高的任務,以便同時觀察到它們的執行情況。

基本概念
基本概念

會話

新建會話

新建一個tmux session非常簡單,語法為tmux new -s session-name,也可以簡寫為tmux,為了方便管理,建議指定會話名稱,如下。

tmux # 新建一個無名稱的會話
tmux new -s demo # 新建一個名稱為demo的會話複製程式碼

斷開當前會話

會話中操作了一段時間,我希望斷開會話同時下次還能接著用,怎麼做?此時可以使用detach命令。

tmux detach # 斷開當前會話,會話在後臺執行複製程式碼

也許你覺得這個太麻煩了,是的,tmux的會話中,我們已經可以使用tmux快捷鍵了。使用快捷鍵組合Ctrl+b + d,三次按鍵就可以斷開當前會話。

進入之前的會話

斷開會話後,想要接著上次留下的現場繼續工作,就要使用到tmux的attach命令了,語法為tmux attach-session -t session-name,可簡寫為tmux a -t session-nametmux a。通常我們使用如下兩種方式之一即可:

tmux a # 預設進入第一個會話
tmux a -t demo # 進入到名稱為demo的會話複製程式碼

關閉會話

會話的使命完成後,一定是要關閉的。我們可以使用tmux的kill命令,kill命令有kill-panekill-serverkill-sessionkill-window共四種,其中kill-session的語法為tmux kill-session -t session-name。如下:

tmux kill-session -t demo # 關閉demo會話
tmux kill-server # 關閉伺服器,所有的會話都將關閉複製程式碼

檢視所有的會話

管理會話的第一步就是要檢視所有的會話,我們可以使用如下命令:

tmux list-session # 檢視所有會話
tmux ls # 檢視所有會話,提倡使用簡寫形式複製程式碼

如果剛好處於會話中怎麼辦?別擔心,我們可以使用對應的tmux快捷鍵Ctrl+b + s,此時tmux將開啟一個會話列表,按上下鍵(⬆︎⬇︎)或者滑鼠滾輪,可選中目標會話,按左右鍵(⬅︎➜)可收起或展開會話的視窗,選中目標會話或視窗後,按Enter鍵即可完成切換。

檢視會話
檢視會話

Tmux快捷指令

關於快捷指令,首先要認識到的是:tmux的所有指令,都包含同一個字首,預設為Ctrl+b,輸入完字首過後,控制檯啟用,命令按鍵才能生效。前面tmux會話相關的操作中,我們共用到了兩個快捷鍵Ctrl+b + dCtrl+b + s,但這僅僅是冰山一角,欲窺tmux龐大的快捷鍵體系,請看下錶。

表一:系統指令。

字首 指令 描述
Ctrl+b ? 顯示快捷鍵幫助文件
Ctrl+b d 斷開當前會話
Ctrl+b D 選擇要斷開的會話
Ctrl+b Ctrl+z 掛起當前會話
Ctrl+b r 強制過載當前會話
Ctrl+b s 顯示會話列表用於選擇並切換
Ctrl+b : 進入命令列模式,此時可直接輸入ls等命令
Ctrl+b [ 進入複製模式,按q退出
Ctrl+b ] 貼上複製模式中複製的文字
Ctrl+b ~ 列出提示資訊快取

表二:視窗(window)指令。

字首 指令 描述
Ctrl+b c 新建視窗
Ctrl+b & 關閉當前視窗(關閉前需輸入y or n確認)
Ctrl+b 0~9 切換到指定視窗
Ctrl+b p 切換到上一視窗
Ctrl+b n 切換到下一視窗
Ctrl+b w 開啟視窗列表,用於且切換視窗
Ctrl+b , 重新命名當前視窗
Ctrl+b . 修改當前視窗編號(適用於視窗重新排序)
Ctrl+b f 快速定位到視窗(輸入關鍵字匹配視窗名稱)

表三:皮膚(pane)指令。

字首 指令 描述
Ctrl+b " 當前皮膚上下一分為二,下側新建皮膚
Ctrl+b % 當前皮膚左右一分為二,右側新建皮膚
Ctrl+b x 關閉當前皮膚(關閉前需輸入y or n確認)
Ctrl+b z 最大化當前皮膚,再重複一次按鍵後恢復正常(v1.8版本新增)
Ctrl+b ! 將當前皮膚移動到新的視窗開啟(原視窗中存在兩個及以上皮膚有效)
Ctrl+b ; 切換到最後一次使用的皮膚
Ctrl+b q 顯示皮膚編號,在編號消失前輸入對應的數字可切換到相應的皮膚
Ctrl+b { 向前置換當前皮膚
Ctrl+b } 向後置換當前皮膚
Ctrl+b Ctrl+o 順時針旋轉當前視窗中的所有皮膚
Ctrl+b 方向鍵 移動游標切換皮膚
Ctrl+b o 選擇下一皮膚
Ctrl+b 空格鍵 在自帶的皮膚佈局中迴圈切換
Ctrl+b Alt+方向鍵 以5個單元格為單位調整當前皮膚邊緣
Ctrl+b Ctrl+方向鍵 以1個單元格為單位調整當前皮膚邊緣(Mac下被系統快捷鍵覆蓋)
Ctrl+b t 顯示時鐘

tmux的絲滑分屏功能正是得益於以上系統、視窗、皮膚的快捷指令,只要你願意,你就可以解除任意的快捷指令,然後綁上你喜歡的指令,當然這就涉及到它的可配置性了,請繼續往下讀。

靈活的配置性

除了快捷指令外,tmux還提供了類似vim的配置性功能。可配置性是軟體的一項進階級功能,只有具備了可配置性,軟體才有了鮮活的個性,使用者才能體會到操作的快感。

修改指令字首

相信只要你用過幾次tmux,就會發現Ctrl+b指令字首,著實不太方便。這兩個鍵相距太遠,按鍵成本太高了。因此我們首先需要將它更換為距離更近的Ctrl+a組合鍵,或者不常用的 ` 鍵(當然其他鍵也是可以的)。

tmux的使用者級配置檔案為~/.tmux.conf(沒有的話就建立一個),修改快捷指令,只需要增加如下三行即可。

set -g prefix C-a #
unbind C-b # C-b即Ctrl+b鍵,unbind意味著解除繫結
bind C-a send-prefix # 繫結Ctrl+a為新的指令字首

# 從tmux v1.6版起,支援設定第二個指令字首
set-option -g prefix2 ` # 設定一個不常用的`鍵作為指令字首,按鍵更快些複製程式碼

修改的~/.tmux.conf配置檔案有如下兩種方式可以令其生效:

  • restart tmux。
  • 在tmux視窗中,先按下Ctrl+b指令字首,然後按下系統指令:,進入到命令模式後輸入source-file ~/.tmux.conf,回車後生效。

既然快捷指令如此方便,更為優雅的做法是新增一個載入配置檔案的快捷指令 ,這樣就可以隨時隨地load新的配置了,如下所示。

# 繫結快捷鍵為r
bind r source-file ~/.tmux.conf \; display-message "Config reloaded.."複製程式碼

請特別注意,在已經建立的視窗中,即使載入了新的配置,舊的配置依然有效(只要你新加的功能沒有覆蓋舊的配置,因此如果你第一次繫結快捷指令為x鍵,然後又改為繫結y鍵,那麼xy都將有效),新建會話不受此影響,將直接採用新的配置。

既然我們已經邁出配置化的第一步,那麼接下來我們可以做得更多。

新增皮膚

tmux中,使用最多的功能之一就是新增一個皮膚。水平方向新增皮膚的指令是 prefix + " ,垂直方向是 prefix + %"%需要兩個鍵同時按下才能完成,加上指令字首至少需要3~4次按鍵才能組成一個完整的指令,同時這個兩個鍵也不夠醒目和方便,因此我們可以繫結兩個更常用的指令 -|,如下所示:

unbind '"'
bind - splitw -v -c '#{pane_current_path}' # 垂直方向新增皮膚,預設進入當前目錄
unbind %
bind | splitw -h -c '#{pane_current_path}' # 水平方向新增皮膚,預設進入當前目錄複製程式碼

開啟滑鼠支援

預設情況下,tmux的多視窗之間的切換以及皮膚大小調整,需要輸入指令才能完成,這一過程,涉及到的指令較多,而且操作麻煩,特別是皮膚大小調整,指令難以一步到位,這個時候開啟滑鼠支援就完美了。

對於tmux v2.1(2015.10.28)之前的版本,需加入如下配置:

setw -g mode-mouse on # 支援滑鼠選取文字等
setw -g mouse-resize-pane on # 支援滑鼠拖動調整皮膚的大小(通過拖動皮膚間的分割線)
setw -g mouse-select-pane on # 支援滑鼠選中並切換皮膚
setw -g mouse-select-window on # 支援滑鼠選中並切換視窗(通過點選狀態列視窗名稱)複製程式碼

有的地方可能會出現set-window-option的寫法,setw就是它的別名。

對於tmux v2.1及以上的版本,僅需加入如下配置:

set-option -g mouse on # 等同於以上4個指令的效果複製程式碼

需要注意的是,開啟滑鼠支援後,iTem2預設的滑鼠選中即複製功能需要同時按下 Alt 鍵,才會生效。

快速皮膚切換

滑鼠支援確實能帶來很大的便捷性,特別是對於習慣了滑鼠操作的tmux新手,但對於鍵盤愛好者而言,這不是什麼好訊息,對他們而言,雙手不離鍵盤是基本素質。

雖然指令字首加方向鍵可以切換皮膚,但方向鍵太遠,不夠快,不夠Geek。沒關係,我們可以將皮膚切換升級為熟悉的hjkl鍵位。

# 繫結hjkl鍵為皮膚切換的上下左右鍵
bind -r k select-pane -U # 繫結k為↑
bind -r j select-pane -D # 繫結j為↓
bind -r h select-pane -L # 繫結h為←
bind -r l select-pane -R # 繫結l為→複製程式碼

-r表示可重複按鍵,大概500ms之內,重複的hjkl按鍵都將有效,完美支援了快速切換的Geek需求。

除了上下左右外, 還有幾個快捷指令可以設定。

bind -r e lastp # 選擇最後一個皮膚
bind -r ^e last # 選擇最後一個視窗

bind -r ^u swapp -U # 與前一個皮膚交換位置
bind -r ^d swapp -D # 與後一個皮膚交換位置複製程式碼

皮膚大小調整

習慣了全鍵盤操作後,命令的便捷性不言而喻。既然皮膚切換的指令都可以升級,皮膚大小調整的指令自然也不能落後。如下配置就可以升級你的操作:

# 繫結Ctrl+hjkl鍵為皮膚上下左右調整邊緣的快捷指令
bind -r ^k resizep -U 10 # 繫結Ctrl+k為往↑調整皮膚邊緣10個單元格
bind -r ^j resizep -D 10 # 繫結Ctrl+j為往↓調整皮膚邊緣10個單元格
bind -r ^h resizep -L 10 # 繫結Ctrl+h為往←調整皮膚邊緣10個單元格
bind -r ^l resizep -R 10 # 繫結Ctrl+l為往→調整皮膚邊緣10個單元格複製程式碼

以上,resizepresize-pane的別名。

皮膚最大化

當視窗中皮膚的數量逐漸增多時,每個皮膚的空間就會逐漸減少。為了保證有足夠的空間顯示內容,tmux從v1.8版本起,提供了皮膚的最大化功能,輸入tmux-prefix+z,就可以最大化當前皮膚至視窗大小,只要再重複輸入一次,便恢復正常。那麼tmux v1.8以下的版本,怎麼辦呢?別急,有大神提供瞭如下的解決方案。

首先編寫一個zoom指令碼,該指令碼通過新建一個視窗,交換當前皮膚與新的視窗預設皮膚位置,來模擬最大的功能;通過重複一次按鍵,還原皮膚位置,並關閉新建的視窗,來模擬還原功能,如下所示:

#!/bin/bash -f
currentwindow=`tmux list-window | tr '\t' ' ' | sed -n -e '/(active)/s/^[^:]*: *\([^ ]*\) .*/\1/gp'`;
currentpane=`tmux list-panes | sed -n -e '/(active)/s/^\([^:]*\):.*/\1/gp'`;
panecount=`tmux list-panes | wc | sed -e 's/^ *//g' -e 's/ .*$//g'`;
inzoom=`echo $currentwindow | sed -n -e '/^zoom/p'`;
if [ $panecount -ne 1 ]; then
    inzoom="";
fi
if [ $inzoom ]; then
    lastpane=`echo $currentwindow | rev | cut -f 1 -d '@' | rev`;
    lastwindow=`echo $currentwindow | cut -f 2- -d '@' | rev | cut -f 2- -d '@' | rev`;
    tmux select-window -t $lastwindow;
    tmux select-pane -t $lastpane;
    tmux swap-pane -s $currentwindow;
    tmux kill-window -t $currentwindow;
else
    newwindowname=zoom@$currentwindow@$currentpane;
    tmux new-window -d -n $newwindowname;
    tmux swap-pane -s $newwindowname;
    tmux select-window -t $newwindowname;
fi複製程式碼

不妨將該指令碼存放在~/.tmux目錄中(沒有則新建目錄),接下來只需要繫結一個快捷指令就行,如下。

unbind z
bind z run ". ~/.tmux/zoom"複製程式碼

視窗變為皮膚

通過上面的zoom指令碼,皮膚可以輕鬆地最大化為一個新的視窗。那麼反過來,視窗是不是可以最小化為一個皮膚呢?

試想這樣一個場景:當你開啟多個視窗後,然後想將其中幾個視窗合併到當前視窗中,以便對比觀察輸出。

實際上,你的要求就是將其它視窗變成皮膚,然後合併到當前視窗中。對於這種操作,我們可以在當前視窗,按下prefix + :,開啟命令列,然後輸入如下命令:

join-pane -s window01 # 合併名稱為window01的視窗的預設(第一個)皮膚到當前視窗中
join-pane -s window01.1 # .1顯式指定了第一個皮膚,.2就是第二個皮膚(我本地將皮膚編號起始值設定為1,預設是0)複製程式碼

每次執行join-pane命令都會合並一個皮膚,並且指定的視窗會減少一個皮膚,直到皮膚數量為0,視窗關閉。

除了在當前會話中操作外,join-pane命令甚至可以從其它指定會話中合併皮膚,格式為join-pane -s [session_name]:[window].[pane],如join-pane -s 2:1.1 即合併第二個會話的第一個視窗的第一個皮膚到當前視窗,當目標會話的視窗和皮膚數量為0時,會話便會關閉。

注:上一節中的swap-pane命令與join-pane語法基本一致。

其他配置

bind m command-prompt "splitw -h 'exec man %%'"   # 繫結m鍵為在新的panel開啟man
# 繫結P鍵為開啟日誌功能,如下,皮膚的輸出日誌將儲存到桌面
bind P pipe-pane -o "cat >>~/Desktop/#W.log" \; display "Toggled logging to ~/Desktop/#W.log"複製程式碼

恢復使用者空間

tmux會話中,Mac的部分命令如 osascriptopenpbcopypbpaste等可能會失效(失效命令未列全)。

部分bug列表如下:

對此,我們可以通過安裝reattach-to-user-namespace包裝程式來解決這個問題。

brew install reattach-to-user-namespace複製程式碼

~/.tmux.conf中新增配置:

set -g default-command "reattach-to-user-namespace -l $SHELL"複製程式碼

這樣你的互動式shell最終能夠重新連線到使用者級的名稱空間。由於連線狀態能夠被子程式繼承,故以上配置保證了所有從 shell 啟動的命令能夠被正確地連線。

有些時候,我們可能會在不同的作業系統中共享配置檔案,如果你的tmux版本大於1.9,我們還可以使用if-shell來判斷是否Mac系統,然後再指定default-command

if-shell 'test "$(uname -s)" = Darwin' 'set-option -g default-command "exec reattach-to-user-namespace -l $SHELL"'複製程式碼

對於tmux v1.8及更早的版本,可以使用如下包裝後的配置:

set-option -g default-command 'command -v reattach-to-user-namespace >/dev/null && exec reattach-to-user-namespace -l "$SHELL" || exec "$SHELL"'複製程式碼

以上,$SHELL對應於你的預設Shell,通常是/usr/bin/bash/usr/local/bin/zsh

複製模式

tmux中操作文字,自然離不開復制模式,通常使用複製模式的步驟如下:

  1. 輸入 `+[ 進入複製模式
  2. 按下 空格鍵 開始複製,移動游標選擇複製區域
  3. 按下 Enter鍵 複製選中文字並退出複製模式
  4. 按下 `+] 貼上文字

檢視複製模式預設的快捷鍵風格:

tmux show-window-options -g mode-keys # mode-keys emacs複製程式碼

預設情況下,快捷鍵為emacs風格。

為了讓複製模式更加方便,我們可以將快捷鍵設定為熟悉的vi風格,如下:

setw -g mode-keys vi # 開啟vi風格後,支援vi的C-d、C-u、hjkl等快捷鍵複製程式碼

自定義複製和選擇快捷鍵

除了快捷鍵外,複製模式的啟用、選擇、複製、貼上等按鍵也可以向vi風格靠攏。

bind Escape copy-mode # 繫結esc鍵為進入複製模式
bind -t vi-copy v begin-selection # 繫結v鍵為開始選擇文字
bind -t vi-copy y copy-selection # 繫結y鍵為複製選中文字
bind p pasteb # 繫結p鍵為貼上文字(p鍵預設用於進入上一個視窗,不建議覆蓋)複製程式碼

以上,繫結 vy兩鍵的設定只在tmux v2.4版本以下才有效,對於v2.4及以上的版本,繫結快捷鍵需要使用 -T 選項,傳送指令需要使用 -X 選項,請參考如下設定:

bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel複製程式碼

Buffer快取

tmux複製操作的內容預設會存進buffer裡,buffer是一個貼上快取區,新的快取總是位於棧頂,它的操作命令如下:

tmux list-buffers # 展示所有的 buffers
tmux show-buffer [-b buffer-name] # 顯示指定的 buffer 內容
tmux choose-buffer # 進入 buffer 選擇頁面(支援jk上下移動選擇,回車選中並貼上 buffer 內容到皮膚上)
tmux set-buffer # 設定buffer內容
tmux load-buffer [-b buffer-name] file-path # 從檔案中載入文字到buffer快取
tmux save-buffer [-a] [-b buffer-name] path # 儲存tmux的buffer快取到本地
tmux paste-buffer # 貼上buffer內容到會話中
tmux delete-buffer [-b buffer-name] # 刪除指定名稱的buffer複製程式碼

以上buffer操作在不指定buffer-name時,預設處理是棧頂的buffer快取。

在tmux會話的命令列輸入時,可以省略上述tmux字首,其中list-buffers的操作如下所示:

list-buffers
list-buffers

choose-buffer的操作如下所示:

choose-buffer
choose-buffer

預設情況下,buffers內容是獨立於系統貼上板的,它存在於tmux程式中,且可以在會話間共享。

使用系統貼上板

存在於tmux程式中的buffer快取,雖然可以在會話間共享,但不能直接與系統貼上板共享,不免有些遺憾。幸運的是,現在我們有成熟的方案來實現這個功能。

在Linux上使用貼上板

通常,Linux中可以使用xclip工具來接入系統貼上板。

首先,需要安裝xclip

sudo apt-get install xclip複製程式碼

然後,.tmux.conf的配置如下。

# buffer快取複製到Linux系統貼上板
bind C-c run " tmux save-buffer - | xclip -i -sel clipboard"
# Linux系統貼上板內容複製到會話
bind C-v run " tmux set-buffer \"$(xclip -o -sel clipboard)\"; tmux paste-buffer"複製程式碼

按下prefix + Ctrl + c 鍵,buffer快取的內容將通過xlip程式複製到貼上板,按下prefix + Ctrl + v鍵,tmux將通過xclip訪問貼上板,然後由set-buffer命令設定給buffer快取,最後由paste-buffer貼上到tmux會話中。

在Mac上使用貼上板

我們都知道,Mac自帶 pbcopypbpaste命令,分別用於複製和貼上,但在tmux命令中它們卻不能正常執行。這裡我將詳細介紹下原因:

Mac的貼上板服務是在引導名稱空間註冊的。名稱空間存在層次之分,更高階別的名稱空間擁有訪問低階別名稱空間(如root引導名稱空間)的許可權,反之卻不行。流程建立的屬於Mac登入會話的一部分,它會被自動包含在使用者級的引導名稱空間中,因此只有使用者級的名稱空間才能訪問貼上板服務。tmux使用守護程式(3)庫函式建立其伺服器程式,在Mac OS X 10.5中,蘋果改變了守護程式(3)的策略,將生成的過程從最初的引導名稱空間移到了根引導名稱空間。而根引導名稱空間訪問許可權較低,這意味著tmux伺服器,和它的子程式,一同失去了原引導名稱空間的訪問許可權(即無許可權訪問貼上板服務)。

如此,我們可以使用一個小小的包裝程式來重新連線到合適的名稱空間,然後執行訪問使用者級名稱空間的貼上板服務,這個包裝程式就是reattach-to-user-namespace

那麼,Mac下.tmux.conf的配置如下:

# buffer快取複製到Mac系統貼上板
bind C-c run "tmux save-buffer - | reattach-to-user-namespace pbcopy"
# Mac系統貼上板內容複製到會話
bind C-v run "reattach-to-user-namespace pbpaste | tmux load-buffer - \; paste-buffer -d"複製程式碼

reattach-to-user-namespace 作為包裝程式來訪問Mac貼上板,按下prefix + Ctrl + c 鍵,buffer快取的內容將複製到貼上板,按下prefix + Ctrl + v鍵,貼上板的內容將通過 load-buffer 載入,然後由 paste-buffer 貼上到tmux會話中。

為了在複製模式中使用Mac系統的貼上板,可做如下配置:

# 繫結y鍵為複製選中文字到Mac系統貼上板
bind-key -T copy-mode-vi 'y' send-keys -X copy-pipe-and-cancel 'reattach-to-user-namespace pbcopy'
# 滑鼠拖動選中文字,並複製到Mac系統貼上板
bind-key -T copy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel "pbcopy"複製程式碼

完成以上配置後記得重啟tmux伺服器。至此,複製模式中,按y鍵將儲存選中的文字到Mac系統貼上板,隨後按Command + v鍵便可貼上。

儲存Tmux會話

資訊時代,資料尤為重要。tmux保護現場的能力依賴於tmux程式,如果程式退出,則意味著會話資料的丟失,因此關機重啟後,tmux中的會話將被清空,這不是我們想要見到的。幸運的是,目前有這樣兩款外掛:Tmux ResurrectTmux Continuum,可以永久儲存tmux會話(它們均適用於tmux v1.9及以上版本)。

Tmux Resurrect

Tmux Resurrect無須任何配置,就能夠備份tmux會話中的各種細節,包括視窗、皮膚的順序、佈局、工作目錄,執行程式等等資料。因此它能在系統重啟後完全地恢復會話。由於其冪等的恢復機制,它不會試圖去恢復一個已經存在的視窗或者皮膚,所以,即使你不小心多恢復了幾次會話,它也不會出現問題,這樣主動恢復時我們就不必擔心手抖多按了一次。另外,如果你是tmuxinator使用者,我也建議你遷移到 tmux-resurrect外掛上來,具體請參考Migrating from tmuxinator

Tmux Resurrec安裝過程如下所示:

cd ~/.tmux
mkdir plugins
git clone https://github.com/tmux-plugins/tmux-resurrect.git複製程式碼

安裝後需在~/.tmux.conf中增加一行配置:

run-shell ~/.tmux/plugins/tmux-resurrect/resurrect.tmux複製程式碼

至此安裝成功,按下prefix + r過載tmux配置。

Tmux Resurrec提供如下兩個操作:

  • 儲存,快捷指令是prefix + Ctrl + s,tmux狀態列在儲存開始,儲存後分別提示"Saving...","Tmux environment saved !"。
  • 恢復,快捷指令是prefix + Ctrl + r,tmux狀態列在恢復開始,恢復後分別提示"Restoring...","Tmux restore complete !"。

儲存時,tmux會話的詳細資訊會以文字檔案的格式儲存到~/.tmux/resurrect目錄,恢復時則從此處讀取,由於資料檔案是明文的,因此你完全可以自由管理或者編輯這些會話狀態檔案(如果備份頻繁,記得定期清除歷史備份)。

可選的配置

Tmux Resurrec本身是免配置開箱即用的,但同時也提供瞭如下選項以便修改其預設設定。

set -g @resurrect-save 'S' # 修改儲存指令為S
set -g @resurrect-restore 'R' 修改恢復指令為R
# 修改會話資料的保持路徑,此處不能使用除了$HOME, $HOSTNAME, ~之外的環境變數
set -g @resurrect-dir '/some/path'複製程式碼

預設情況下只有一個保守的列表項(即vi vim nvim emacs man less more tail top htop irssi mutt)可以恢復,對此 Restoring programs doc 解釋了怎麼去恢復額外的專案。

進階的備份

除了基礎備份外,Tmux Resurrec還提供進階的備份功能,如下所示:

  • 恢復vim 和 neovim 會話
  • 恢復皮膚內容
  • 恢復shell的歷史記錄(實驗性功能)

進階的備份功能預設不開啟,需要特別配置。

1)恢復vim 和 neovim 會話,需要完成如下兩步:

  • 通過vim的vim-obsession外掛儲存vim/neovim會話。

    cd ~/.vim/bundle
    git clone git://github.com/tpope/vim-obsession.git
    vim -u NONE -c "helptags vim-obsession/doc" -c q複製程式碼
  • ~/.tmux.conf中增加兩行配置:

    set -g @resurrect-strategy-vim 'session' # for vim
    set -g @resurrect-strategy-nvim 'session' # for neovim複製程式碼

2)恢復皮膚內容,需在~/.tmux.conf中增加一行配置:

set -g @resurrect-capture-pane-contents 'on' # 開啟恢復皮膚內容功能複製程式碼

目前使用該功能時,請確保tmux的default-command沒有包含&& 或者||操作符,否則將導致bug。(檢視default-command的值,請使用命令tmux show -g default-command。)

3)恢復shell的歷史記錄,需在~/.tmux.conf中增加一行配置:

set -g @resurrect-save-shell-history 'on'複製程式碼

由於技術的限制,儲存時,只有無前臺任務執行的皮膚,它的shell歷史記錄才能被儲存。

Tmux Continuum

可能你嫌手動儲存和恢復太過麻煩,別擔心,這不是問題。Tmux Continuum 在 Tmux Resurrec的基礎上更進一步,現在儲存和恢復全部自動化了,如你所願,可以無感使用tmux,不用再擔心備份問題。

Tmux Continuum安裝過程如下所示(它依賴Tmux Resurrect,請保證已安裝Tmux Resurrect外掛):

cd ~/.tmux/plugins
git clone https://github.com/tmux-plugins/tmux-continuum.git複製程式碼

安裝後需在~/.tmux.conf中增加一行配置:

run-shell ~/.tmux/plugins/tmux-continuum/continuum.tmux複製程式碼

Tmux Continuum預設每隔15mins備份一次,我設定的是一天一次:

set -g @continuum-save-interval '1440'複製程式碼

關閉自動備份,只需設定時間間隔為 0 即可:

set -g @continuum-save-interval '0'複製程式碼

想要在tmux啟動時就恢復最後一次儲存的會話環境,需增加如下配置:

set -g @continuum-restore 'on' # 啟用自動恢復複製程式碼

如果不想要啟動時自動恢復的功能了,直接移除上面這行就行。想要絕對確定自動恢復不會發生,就在使用者根目錄下建立一個tmux_no_auto_restore空檔案(建立命令:touch ~/tmux_no_auto_restore),該檔案存在時,自動恢復將不觸發。

對於tmux高階使用者(可能就是你)而言,同時執行多個tmux伺服器也是有可能的。你可能並不希望後面啟用的幾個tmux伺服器自動恢復或者自動儲存會話。因此Tmux Continuum會優先在第一個啟用的tmux伺服器中生效,隨後啟用的tmux伺服器不再享受自動恢復或自動儲存會話的待遇。

實際上,不管Tmux Continuum功能有沒有啟用,或者多久儲存一次,我們都有辦法從狀態列知曉。Tmux Continuum提供了一個檢視執行狀態的插值#{continuum_status},它支援status-rightstatus-left兩種狀態列設定,如下所示:

set -g status-right 'Continuum status: #{continuum_status}'複製程式碼

tmux執行時,#{continuum_status} 將顯示儲存的時間間隔(單位為分鐘),此時狀態列會顯示:

Continuum status: 1440複製程式碼

如果其自動儲存功能關閉了,那麼狀態列會顯示:

Continuum status: off複製程式碼

藉助Tmux Continuum外掛,Mac重啟時,我們甚至可以選擇在Terminal 或者 iTerm2 中自動全屏啟用tmux。

為此,需在~/.tmux.conf中增加一行配置:

set -g @continuum-boot 'on'複製程式碼

Mac下,自動啟用tmux還支援如下選項:

  • set -g @continuum-boot-options 'fullscreen'Terminal自動全屏,tmux命令在Terminal中執行。
  • set -g @continuum-boot-options 'iterm'iTerm2 替換 Terminal 應用,tmux命令在iTerm2中執行。
  • set -g @continuum-boot-options 'iterm,fullscreen'iTerm2自動全屏,tmux命令在iTerm2中執行。

Linux中則沒有這些選項,它只能設定為自動啟用tmux伺服器。

Tpm

以上,我們直接安裝了tmux外掛。這沒有問題,可當外掛越來越多時,我們就會需要統一的外掛管理器。因此官方提供了tpm(支援tmux v1.9及以上版本)。

tpm安裝過程如下所示:

cd ~/.tmux/plugins
git clone https://github.com/tmux-plugins/tpm複製程式碼

安裝後需在~/.tmux.conf中增加如下配置:

# 預設需要引入的外掛
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'

# 引入其他外掛的示例
# set -g @plugin 'github_username/plugin_name' # 格式:github使用者名稱/外掛名
# set -g @plugin 'git@github.com/user/plugin' # 格式:git@github外掛地址

# 初始化tmux外掛管理器(保證這行在~/.tmux.conf的非常靠後的位置)
run '~/.tmux/plugins/tpm/tpm'複製程式碼

然後按下prefix + r過載tmux配置,使得tpm生效。

基於tpm外掛管理器,安裝外掛僅需如下兩步:

  1. ~/.tmux.conf中增加新的外掛,如set -g @plugin '...'
  2. 按下prefix + I鍵下載外掛,並重新整理tmux環境。

更新外掛,請按下prefix + U 鍵,選擇待更新的外掛後,回車確認並更新。

解除安裝外掛,需如下兩步:

  1. ~/.tmux.conf中移除外掛所在行。
  2. 按下prefix + alt + u 移除外掛。

會話共享

結對程式設計

tmux多會話連線實時同步的功能,使得結對程式設計成為了可能,這也是開發者最喜歡的功能之一。現在就差一步了,就是藉助tmate把tmux會話分享出去。

tmate是tmux的管理工具,它可以輕鬆的建立tmux會話,並且自動生成ssh連結。

安裝tmate

brew install tmate複製程式碼

使用tmate新建一個tmux會話

tmate複製程式碼

此時螢幕下方會顯示ssh url,如下所示:

ssh url
ssh url

檢視tmate生成的ssh連結

tmate show-messages複製程式碼

生成的ssh url如下所示,其中一個為只讀,另一個可編輯。

ssh url
ssh url

共享賬號&組會話

使用tmate遠端共享tmux會話,受制於多方的網路質量,必然會存在些許延遲。如果共享會話的多方擁有同一個遠端伺服器的賬號,那麼我們可以使用組會話解決這個問題。

先在遠端伺服器上新建一個公共會話,命名為groupSession

tmux new -s groupSession複製程式碼

其他使用者不去直接連線這個會話,而是通過建立一個新的會話來加入上面的公共會話groupSession

tmux new -t groupSession -s otherSession複製程式碼

此時兩個使用者都可以在同一個會話裡操作,就會好像第二個使用者連線到了groupSession的會話一樣。此時兩個使用者都可以建立新建的視窗,新視窗的內容依然會實時同步,但是其中一個使用者切換到其它視窗,對另外一個使用者沒有任何影響,因此在這個共享的組會話中,使用者各自的操作可以通過新建視窗來執行。即使第二個使用者關閉otherSession會話,共享會話groupSession依然存在。

組會話在共享的同時,又保留了相對的獨立,非常適合結對程式設計場景,它是結對程式設計最簡單的方式,如果賬號不能共享,我們就要使用下面的方案了。

獨立賬號&Socket共享會話

開始之前我們需要確保使用者對遠端伺服器上同一個目錄擁有相同的讀寫許可權,假設這個目錄為/var/tmux/

使用new-session(簡寫new)建立會話時,使用的是預設的socket位置,預設socket無法操作,所以我們需要建立一個指定socket檔案的會話。

tmux -S /var/tmux/sharefile複製程式碼

另一個使用者進入時,需要指定socket檔案加入會話。

tmux -S /var/tmux/sharefile attach複製程式碼

這樣,兩個不同的使用者就可以共享同一個會話了。

通常情況下,不同的使用者使用不同的配置檔案來建立會話,但是,使用指定socket檔案建立的tmux會話,會話載入的是第一個建立會話的使用者的~/.tmux.conf配置檔案,隨後加入會話的其他使用者,依然使用同一份配置檔案。

Tmux優化

要想tmux更加人性化、效能更佳,不妨參考下如下配置。

設定視窗皮膚起始序號

set -g base-index 1 # 設定視窗的起始下標為1
set -g pane-base-index 1 # 設定皮膚的起始下標為1複製程式碼

自定義狀態列

set -g status-utf8 on # 狀態列支援utf8
set -g status-interval 1 # 狀態列重新整理時間
set -g status-justify left # 狀態列列表左對齊
setw -g monitor-activity on # 非當前視窗有內容更新時在狀態列通知

set -g status-bg black # 設定狀態列背景黑色
set -g status-fg yellow # 設定狀態列前景黃色
set -g status-style "bg=black, fg=yellow" # 狀態列前景背景色

set -g status-left "#[bg=#FF661D] ❐ #S " # 狀態列左側內容
set -g status-right 'Continuum status: #{continuum_status}' # 狀態列右側內容
set -g status-left-length 300 # 狀態列左邊長度300
set -g status-right-length 500 # 狀態列左邊長度500

set -wg window-status-format " #I #W " # 狀態列視窗名稱格式
set -wg window-status-current-format " #I:#W#F " # 狀態列當前視窗名稱格式(#I:序號,#w:視窗名稱,#F:間隔符)
set -wg window-status-separator "" # 狀態列視窗名稱之間的間隔
set -wg window-status-current-style "bg=red" # 狀態列當前視窗名稱的樣式
set -wg window-status-last-style "fg=red" # 狀態列最後一個視窗名稱的樣式

set -g message-style "bg=#202529, fg=#91A8BA" # 指定訊息通知的前景、後景色複製程式碼

開啟256 colors支援

預設情況下,tmux中使用vim編輯器,文字內容的配色和直接使用vim時有些差距,此時需要開啟256 colors的支援,配置如下。

set -g default-terminal "screen-256color"複製程式碼

或者:

set -g default-terminal "tmux-256color"複製程式碼

或者啟動tmux時增加引數-2

alias tmux='tmux -2' # Force tmux to assume the terminal supports 256 colours複製程式碼

關閉預設的rename機制

tmux預設會自動重新命名視窗,頻繁的命令列操作,將頻繁觸發重新命名,比較浪費CPU效能,效能差的計算機上,問題可能更為明顯。建議新增如下配置關閉rename機制。

setw -g automatic-rename off
setw -g allow-rename off複製程式碼

去掉小圓點

tmux預設會同步同一個會話的操作到所有會話連線的終端視窗中,這種同步機制,限制了視窗的大小為最小的會話連線。因此當你開一個大視窗去連線會話時,實際的視窗將自動調整為最小的那個會話連線的視窗,終端剩餘的空間將填充排列整齊的小圓點,如下所示。

dot
dot

為了避免這種問題,我們可以在連線會話的時候,斷開其他的會話連線。

tmux a -d複製程式碼

如果已經進入了tmux會話中,才發現這種問題,這個時候可以輸入命令達到同樣的效果。

`: a -d複製程式碼

remove dot
remove dot

指令碼化的Tmux

tmux作為終端複用軟體,支援純命令列操作也是其一大亮點。你既可以啟用視覺化介面建立會話,也可以執行指令碼生成會話,對於tmux依賴者而言,編寫幾個tmux指令碼批量維護會話列表,快速重啟、切換、甚至分享部分會話都是非常方便的。可能會有人說為什麼不用Tmux Resurrect呢?是的,Tmux Resurrect很好,一鍵恢復也很誘人,但是對於一個維護大量tmux會話的使用者而言,一鍵恢復可能不見得好,分批次恢復可能是他(她)更想要的,指令碼化的tmux就很好地滿足了這點。

指令碼中建立tmux會話時,由於不需要開啟視覺化介面,需要輸入-d引數指定會話後臺執行,如下。

tmux new -s init -d # 後臺建立一個名稱為init的會話複製程式碼

新建的會話,建議重命令會話的視窗名稱,以便後續維護。

# 重新命名init會話的第一個視窗名稱為service
tmux rename-window -t "init:1" service複製程式碼

現在,可以在剛才的視窗中輸入指令了。

# 切換到指定目錄並執行python服務
tmux send -t "init:service" "cd ~/workspace/language/python/;python2.7 server.py" Enter複製程式碼

一個皮膚佔用一個視窗可能太浪費了,我們來分個屏吧。

# 預設上下分屏
tmux split-window -t "init:service"
# 切換到指定目錄並執行node服務
tmux send -t "init:service" 'cd ~/data/louiszhai/node-webserver/;npm start' Enter複製程式碼

現在一個視窗擁有上下兩個皮膚,是時候建立一個新的視窗來執行更多的程式了。

# 新建一個名稱為tool的視窗
tmux neww -a -n tool -t init # neww等同於new window
# 執行weinre除錯工具
tmux send -t "init:tool" "weinre --httpPort 8881 --boundHost -all-" Enter複製程式碼

另外新建視窗執行程式,有更方便的方式,比如使用 processes 選項。

tmux neww-n processes ls # 新建視窗並執行命令,命令執行結束後視窗將關閉
tmux neww-n processes top # 由於top命令持續在前臺執行,因此視窗將保留,直到top命令退出複製程式碼

新的視窗,我們嘗試下水平分屏。

# 水平分屏
tmux split-window -h -t "init:tool"
# 切換到指定目錄並啟用aria2 web管理後臺
tmux send -t "init:tool" "cd ~/data/tools/AriaNg/dist/;python -m SimpleHTTPServer 10108" Enter複製程式碼

類似的指令碼,我們可以編寫一打,這樣快速重啟、切換、甚至分享會話都將更加便捷。

開機自動啟用Web伺服器

開機自動準備工作環境是一個很好的idea,但卻不好實現。對於程式設計師而言,一個開機即用的計算機會節省大量的初始化操作,特別是前端工程師,本地常常會啟用多個伺服器,每次開機挨個啟動將耗時耗力。為此,在遇到tmux之前,我常常拖延重啟計算機的時機,一度連續執行Mac一月之久,直到它不堪重負。

有了tmux指令碼化的基礎,開機自動啟用web伺服器就不在話下了,接杯水的時間,計算機就重啟恢復了滿血。如下是操作步驟:

首先,上面的tmux指令碼,可以合併到同一個檔案中,指定檔案許可權為可執行,並命名為init.sh(名稱可自取)。

chmod u+x ./init.sh複製程式碼

然後,開啟 系統偏好設定 - 使用者與群組 - 登入項,點選新增按鈕+,選擇剛剛儲存的init.sh指令碼,最終效果如下:

init.sh
init.sh

至此,Mac開機將自動執行 init.sh 指令碼,自動啟用web伺服器。

完成了上面這些配置,就真正實現了一鍵開機。

最後,附上我本地的配置檔案 .tmux.conf,以及啟動指令碼 init.sh


版權宣告:轉載需註明作者和出處。

本文作者: louis

本文連結: louiszhai.github.io/2017/09/30/…

相關文章

相關文章