7  多工管理:tmux 深入指南

tmux 是終端多工器,讓你在單一終端視窗中管理多個 session、window 和 pane。

7.1 為什麼需要 tmux?

  • 持久化 session:SSH 斷線後 session 還在
  • 多視窗管理:一個終端開多個工作區
  • 分割畫面:同時看程式碼和執行結果
  • 腳本化:工作環境可以腳本化

7.2 核心概念

tmux
├── Session 1 (project-a)
│   ├── Window 1 (editor)
│   │   ├── Pane 1 (nvim)
│   │   └── Pane 2 (terminal)
│   └── Window 2 (server)
└── Session 2 (project-b)

7.3 基本設定

7.3.1 背景(問題發現)

tmux 的預設設定在實際使用中會遇到以下問題:

  1. prefix 鍵不順手:預設的 Ctrl+B 在鍵盤上位置較遠,頻繁按壓不便
  2. 編輯模式不熟悉:預設的 emacs 模式對 vim 使用者來說不直覺
  3. 視覺回饋不佳:缺少滑鼠支援和顯色支援降低使用體驗
  4. 操作延遲:ESC 鍵延遲在 vim 中造成困擾
  5. 編號從 0 開始:不符合鍵盤數字鍵的直覺(1 在 0 左邊)

7.3.2 方法

透過 ~/.tmux.conf 設定檔調整 tmux 行為,核心概念包括:

  • Prefix 重新綁定:使用 unbind 解除舊綁定,set-option 設定新 prefix
  • 模式切換mode-keys 控制複製模式的鍵位映射
  • 滑鼠整合mouse on 啟用點擊、拖曳、滾動等滑鼠操作
  • 終端相容性default-terminal 確保顯色正確
  • 效能調校escape-time 減少 ESC 鍵延遲
  • 索引調整base-index 讓視窗編號與鍵盤數字鍵對應

7.3.3 結果(程式碼)

# ~/.tmux.conf

# 改用 Ctrl+A 作為 prefix
unbind C-b
set-option -g prefix C-a
bind C-a send-prefix

# 使用 vi 模式
setw -g mode-keys vi

# 啟用滑鼠
set -g mouse on

# 256 色支援
set -g default-terminal "screen-256color"

# 減少 escape 延遲
set -sg escape-time 0

# 視窗和 pane 從 1 開始編號
set -g base-index 1
set -g pane-base-index 1

7.3.4 討論/延伸

注意事項

  • Ctrl+AGNU Screen 的慣例,但會與 shell 中「移到行首」的快捷鍵衝突。若需要原功能,按兩次 prefix 即可(C-a C-a
  • 若使用 iTerm2 或 Alacritty,可能需要額外設定 set -g default-terminal "tmux-256color" 以獲得更好的顯色支援
  • escape-time 0 雖然解決延遲問題,但某些終端可能需要保留小量延遲(如 set -sg escape-time 10

進階設定

  • True color 支援:set -ga terminal-overrides ",xterm-256color:Tc"
  • 狀態列更新頻率:set -g status-interval 1
  • 歷史記錄行數:set -g history-limit 50000

7.4 常用快捷鍵

7.4.1 Session 管理

快捷鍵 功能
prefix d 離開 session(detach)
prefix s 列出 sessions
prefix $ 重新命名 session

7.4.2 Window 管理

快捷鍵 功能
prefix c 新增 window
prefix , 重新命名 window
prefix n/p 下/上一個 window
prefix [0-9] 切換到指定 window

7.4.3 Pane 管理

快捷鍵 功能
prefix b 水平分割
prefix v 垂直分割
prefix h/j/k/l 切換 pane(vim 風格)
prefix H/J/K/L 調整 pane 大小

7.5 自訂快捷鍵

7.5.1 背景(問題發現)

tmux 預設的快捷鍵設計有以下痛點:

  1. 分割視窗失去路徑:使用 %" 分割時,新 pane 會回到家目錄,而非當前工作目錄
  2. 切換 pane 不直覺:預設使用方向鍵或 o 鍵,對 vim 使用者不友善
  3. 調整大小繁瑣:預設需要 prefix :resize-pane -D 等命令,效率低下
  4. 記憶負擔% 是水平還是垂直?容易混淆

7.5.2 方法

透過 bind 指令重新定義快捷鍵,核心概念:

  • 路徑繼承:使用 #{pane_current_path} 變數保持當前目錄
  • 語意化按鍵b (beside) 水平分割、v (vertical) 垂直分割
  • Vim 風格導航hjkl 對應左下上右,與 vim 移動鍵一致
  • 可重複按鍵bind -r 允許單次 prefix 後連續按鍵
  • 大小寫區分:小寫選擇、大寫調整大小

7.5.3 結果(程式碼)

# 分割視窗時保持當前目錄
bind b split-window -h -c "#{pane_current_path}"
bind v split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"

# Vim 風格切換 pane
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# 調整 pane 大小
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

7.5.4 討論/延伸

注意事項

  • -r (repeatable) 有預設的 repeat-time(通常 500 ms),可用 set -g repeat-time 1000 調整
  • 這些綁定會覆蓋 tmux 預設的 l (last-window) 功能,若需要可改綁定到其他鍵
  • 調整大小的步長 5 可依個人喜好調整(範圍通常是 1-10)

進階技巧

# 快速切換到上一個 window
bind C-a last-window

# 快速最大化/還原當前 pane
bind m resize-pane -Z

# 同步所有 pane 的輸入(對批次操作很有用)
bind S setw synchronize-panes

# 快速關閉 pane(不需確認)
bind x kill-pane

# 快速分割成 2x2 grid
bind g split-window -h \; split-window -v \; select-pane -L \; split-window -v

記憶技巧

  • b = beside(旁邊)→ 水平分割
  • v = vertical(垂直)→ 垂直分割
  • hjkl = vim 移動鍵 ← ↓ ↑ →

7.6 Neovim 整合

7.6.1 背景(問題發現)

當同時使用 tmux 和 Neovim 時,會遇到導航問題:

  1. 切換邊界模糊:在 tmux pane 和 vim split 之間切換需要不同的快捷鍵
  2. 認知負擔:需要記憶兩套導航系統(tmux 的 prefix h/j/k/l vs vim 的 Ctrl-w h/j/k/l
  3. 效率降低:頻繁在編輯器 split 和終端 pane 間切換時,需要思考當前在哪一層
  4. 工作流中斷:切換方式不一致打斷工作流暢度

7.6.2 方法

使用 vim-tmux-navigator 插件實現無縫導航,核心概念:

  • 統一快捷鍵:在 tmux pane 和 vim split 中使用相同的 Ctrl-h/j/k/l
  • 智慧判斷:插件自動偵測當前是在 vim split 邊緣還是 tmux pane 邊緣
  • 透明切換:使用者無需關心邊界,直接使用方向鍵導航整個工作環境
  • 雙向整合:需要同時在 tmux 和 Neovim 中配置

7.6.3 結果(程式碼)

# 在 tmux.conf 中
set -g @plugin 'christoomey/vim-tmux-navigator'

7.6.4 討論/延伸

完整設定步驟

  1. 安裝 Neovim 插件(使用 lazy.nvim):
{
  'christoomey/vim-tmux-navigator',
  cmd = {
    "TmuxNavigateLeft",
    "TmuxNavigateDown",
    "TmuxNavigateUp",
    "TmuxNavigateRight",
  },
  keys = {
    { "<C-h>", "<cmd>TmuxNavigateLeft<cr>" },
    { "<C-j>", "<cmd>TmuxNavigateDown<cr>" },
    { "<C-k>", "<cmd>TmuxNavigateUp<cr>" },
    { "<C-l>", "<cmd>TmuxNavigateRight<cr>" },
  },
}
  1. 安裝 tmux 插件:在 tmux.conf 中加入上述配置後,按 prefix I 安裝

注意事項

  • 此插件會覆蓋 vim 的 Ctrl-l(清除螢幕),可改用 :nohl 或自訂快捷鍵
  • 在 tmux 的複製模式(copy-mode)下不會觸發導航
  • 若使用 fzf.vim,可能需要額外設定以避免衝突

實際工作流範例

# 典型的開發環境佈局
# ┌──────────────┬──────────┐
# │              │          │
# │   Neovim     │  Git     │
# │   (3 splits) │  status  │
# │              │          │
# ├──────────────┴──────────┤
# │   Development Server    │
# └─────────────────────────┘

# 無縫切換:
# Ctrl-h/j/k/l 在所有區域間自由移動

7.7 插件管理

7.7.1 背景(問題發現)

手動管理 tmux 插件面臨的問題:

  1. 安裝繁瑣:每個插件需要手動 clone 到正確路徑,並在設定檔中 source
  2. 更新困難:需要進入每個插件目錄執行 git pull,容易遺漏
  3. 版本控制:難以追蹤和鎖定插件版本
  4. 設定分散:插件路徑和載入邏輯散落在設定檔各處

7.7.2 方法

使用 TPM (Tmux Plugin Manager) 統一管理插件,核心概念:

  • 宣告式管理:在設定檔中宣告所需插件,TPM 負責下載和載入
  • 快捷鍵操作prefix I 安裝、prefix U 更新、prefix alt-u 移除
  • 自動載入:TPM 在 tmux 啟動時自動載入已安裝的插件
  • GitHub 整合:支援 username/repo 格式,自動從 GitHub 下載

7.7.3 結果(程式碼)

# 安裝 TPM
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

# 在 tmux.conf 中
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'catppuccin/tmux'

# 初始化 [TPM](https://github.com/tmux-plugins/tpm)(放在 tmux.conf 最後)
run '~/.tmux/plugins/tpm/tpm'

7.7.4 討論/延伸

基本操作

  • 安裝插件:在 tmux.conf 中加入 set -g @plugin '...',然後按 prefix I
  • 更新插件:按 prefix U,會列出可更新的插件並選擇性更新
  • 移除插件:從 tmux.conf 移除對應行,按 prefix alt-u
  • 重新載入設定tmux source ~/.tmux.confprefix r(需先綁定)

注意事項

  • run '~/.tmux/plugins/tpm/tpm' 必須放在 tmux.conf 最後一行
  • 初次使用需要手動 clone TPM,之後插件都由 TPM 管理
  • 若 tmux 已啟動,修改插件清單後需要 source 設定檔再按 prefix I

進階用法

# 使用特定分支
set -g @plugin 'user/plugin#branch-name'

# 使用 Git URL(非 GitHub)
set -g @plugin 'https://gitlab.com/user/plugin'

# 指定本地插件路徑
set -g @plugin 'file://~/path/to/plugin'

推薦的插件順序

# 1. TPM 本身(必須)
set -g @plugin 'tmux-plugins/tpm'

# 2. 基礎設定(建議第一個安裝)
set -g @plugin 'tmux-plugins/tmux-sensible'

# 3. 功能型插件
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-yank'

# 4. 外觀主題(建議最後)
set -g @plugin 'catppuccin/tmux'

插件說明

7.8 實用插件推薦

插件 功能
tmux-resurrect 保存/恢復 session
tmux-fzf fzf 整合
catppuccin/tmux 美觀的主題
tmux-yank 系統剪貼簿整合

7.9 實作練習

  1. 建立一個新 session:tmux new -s myproject
  2. 分割成三個 pane:編輯器、終端、log
  3. 練習用快捷鍵切換 pane
  4. 嘗試 detach 再 attach
Tip效率提示

7.9.1 背景(問題發現)

修改 tmux 設定檔後,傳統的做法是:

  1. 退出所有 tmux session
  2. 重新啟動 tmux
  3. 這會導致所有執行中的程式中斷

這對於頻繁調整設定的使用者非常不便。

7.9.2 方法

綁定快捷鍵來動態重新載入設定檔,使用 source-file 命令重新讀取設定:

bind r source-file ~/.tmux.conf \; display 'Reloaded!'

核心概念

  • source-file 重新執行設定檔中的所有命令
  • \; 連接多個 tmux 命令
  • display 顯示提示訊息確認操作完成

7.9.3 實際應用

按下 prefix r 即可立即套用新設定,無需重啟 tmux。

7.9.4 注意事項

  • 某些設定(如 default-terminal)需要重啟 tmux 才會生效
  • 若設定檔有語法錯誤,會顯示錯誤訊息但不會影響現有 session
  • 建議在修改設定前先備份:cp ~/.tmux.conf ~/.tmux.conf.backup

7.10 完整設定範例

結合本章所有概念的完整 ~/.tmux.conf

# ============================================
# 基礎設定
# ============================================
# Prefix 鍵
unbind C-b
set-option -g prefix C-a
bind C-a send-prefix

# 編輯模式
setw -g mode-keys vi
set -g status-keys vi

# 顯示設定
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc"

# 效能調校
set -sg escape-time 0
set -g history-limit 50000
set -g display-time 4000
set -g status-interval 5

# 編號從 1 開始
set -g base-index 1
set -g pane-base-index 1
set -g renumber-windows on

# 滑鼠支援
set -g mouse on

# ============================================
# 快捷鍵綁定
# ============================================
# 重新載入設定
bind r source-file ~/.tmux.conf \; display 'Config reloaded!'

# 分割視窗(保持當前路徑)
bind b split-window -h -c "#{pane_current_path}"
bind v split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"

# Vim 風格 pane 切換
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# Pane 大小調整
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# 快速切換
bind C-a last-window
bind m resize-pane -Z

# ============================================
# 插件管理
# ============================================
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'christoomey/vim-tmux-navigator'
set -g @plugin 'catppuccin/tmux'

# 初始化 [TPM](https://github.com/tmux-plugins/tpm)(必須放在最後)
run '~/.tmux/plugins/tpm/tpm'

7.11 彩蛋:不會消失的浮動終端機

如果你用過 IDE 的「快速終端機」功能(如 VS Code 的 `Ctrl+``),應該會很羨慕那種「按一下叫出來、再按一下收起來」的體驗。

好消息是,tmux 3.2+ 支援 popup 功能,我們可以打造一個持久的浮動終端機

# ~/.tmux.conf
unbind i
bind i if-shell "[[ $(tmux display-message -p '#S') = floating* ]]" {
    detach-client
} {
    run-shell "~/.dotfiles/shellscripts/tmux_popup.sh"
}

搭配這個腳本 tmux_popup.sh

#!/bin/bash
current_session_name=$(tmux display-message -p '#S')
parent_session_dir=$(tmux display-message -p -F "#{pane_current_path}" -t0)
session_name="floating_${current_session_name}"

if tmux has-session -t "$session_name" 2>/dev/null; then
    tmux popup -w 90% -h 80% -E "tmux attach -t $session_name"
else
    tmux new-session -d -s "$session_name" -c "$parent_session_dir"
    tmux popup -w 90% -h 80% -E "tmux attach -t $session_name"
fi

現在按 prefix i 就會彈出一個佔據 90% 畫面的浮動終端機。最棒的是:

  • 不會消失:關掉 popup 後,裡面執行的程式還在跑
  • 繼承路徑:自動在當前工作目錄開啟
  • 獨立 session:每個主 session 有自己的浮動 session
Tip使用情境

這個浮動終端機非常適合:

  • 快速跑個指令,不想打亂現有的 pane 佈局
  • 開一個 htoplazygit 常駐在背景
  • 當作便條紙,隨手記點東西

prefix i 叫出來,再按一次收起來。你值得擁有!