10 Zsh Hacks I Wish I Knew About Sooner|10 個讓你相見恨晚的 Zsh 技巧

作者 Dreams of Code 分享了他使用 Zsh 超過十年累積的 10 個實用技巧,涵蓋命令列編輯、歷史展開、目錄掛鉤、進階別名、批次檔案管理、目錄書籤、自訂 widget 到快捷鍵插入等面向。這些技巧能大幅提升終端機的操作效率,讓 Zsh 的使用體驗更接近一個功能完整的文字編輯器。


原影片連結:https://www.youtube.com/watch?v=3fVAtaGhUyU

影片重點

  • 使用 edit-command-line widget 可在文字編輯器中修改長指令,避免在命令列中費力導航
  • Zsh 內建 undo(Ctrl+_)功能,能撤銷命令列上的誤操作
  • magic-space widget 可在按空白鍵時自動展開歷史指令,避免盲目執行 !!
  • chpwd hook 能在切換目錄時自動執行指令,適用於自動載入 Nix shell、Python 虛擬環境、NVM 版本
  • Suffix alias(alias -s)可根據副檔名自動用指定程式開啟檔案
  • Global alias(alias -g)可在指令任何位置使用別名,簡化重複參數
  • zmv 指令支援 pattern matching 批次重新命名或搬移檔案
  • Named directory(hash -d)可為常用目錄建立書籤捷徑
  • 透過 zle 自訂 widget,可建立清除歷史但保留命令緩衝區等自訂功能
  • 使用 bindkey -s 可設定熱鍵插入常用指令樣板,如 git commit -m ""

詳細內容

[00:00] Edit Command Buffer — 在編輯器中修改長指令

在終端機中輸入長指令時,如果發現幾行之前有錯字,用鍵盤導航回去修改非常沒效率。Zsh 提供了一個叫做 edit-command-line 的 widget,可以把整個命令緩衝區的內容丟進你設定的文字編輯器(例如 Neovim)中編輯,修改完後儲存關閉,變更就會自動套用回命令列。

設定方式只需要在 .zshrc 中加入三行程式碼,將這個 widget 綁定到 Ctrl+X Ctrl+E 即可。這對於需要修改先前指令或處理多行指令的場景特別實用。

[01:42] Undo — Zsh 的撤銷功能

即使有了 edit-command-line,有些操作失誤還是無法靠它挽救。例如按 Ctrl+W 刪除了比預期更多的文字時,可以用 Ctrl+_(Control + 底線)來撤銷上一個操作。Zsh 也有 redo 功能,但預設沒有綁定快捷鍵,作者認為實用性不高所以沒有設定。

[02:32] Magic Space — 安全展開歷史指令

!! 是一個經典指令,用來重新執行上一條指令,搭配 sudo 特別好用。但問題在於如果你不確定上一條指令是什麼,就等於在玩俄羅斯輪盤。

綁定 magic-space widget 到空白鍵後,按下空白鍵時 Zsh 會自動把 !! 等歷史展開語法替換成實際的指令內容,讓你在執行前可以先確認。這個功能也支援按索引引用歷史指令、提取前一指令的參數、或搜尋包含特定字串的歷史指令。如果你用 Oh My Zsh,magic-space 通常已預設啟用。

[04:00] chpwd Hook — 切換目錄時自動執行指令

chpwd hook 允許你在每次切換目錄時自動執行指定的指令。最基本的用法是切換目錄後自動執行 ls 顯示目錄內容。

作者分享了三個實用的 chpwd 應用場景:

  1. Nix shell 自動載入:進入包含 flake.nix 的專案目錄時,自動載入 Nix develop shell
  2. Python 虛擬環境自動啟用:進入包含虛擬環境的目錄時,自動 activate
  3. NVM 版本自動切換:進入包含 .nvmrc 的 Node.js 專案時,自動執行 nvm use

注意 chpwd 只能定義一個 hook function,如果需要多個行為,可以使用 add-zsh-hook 函數將多個函數掛載到同一個 hook 上。

[07:27] Suffix Alias — 根據副檔名自動開啟檔案

使用 alias -s 可以建立 suffix alias,讓你直接輸入檔案名稱就能用指定程式開啟。例如設定 .json 檔案用 jq 開啟、.md 檔案用 bat 開啟、.go 檔案用 Neovim 開啟。

語法是 alias -s go=$EDITOR,之後只要在命令列輸入 main.go,就會自動用 Neovim 開啟。如果明確指定了其他指令,suffix alias 不會介入,只在你沒有指定指令時才會觸發,非常適合設定各種檔案類型的預設開啟方式。

[09:00] Global Alias — 在指令任何位置使用的別名

一般的 alias 只能用在指令開頭,但 alias -g 定義的 global alias 可以用在指令的任何位置。這對於簡化常用的管線參數特別有用。

例如定義 alias -g NE='2>/dev/null',就能在任何指令後面加上 NE 來將 stderr 導向 /dev/null。作者還定義了導向 stdout 和同時導向 stdout+stderr 的 global alias,以及 pipe 到 jq 和複製到剪貼簿的快捷別名。建議使用大寫命名 global alias 以避免衝突。

[10:42] ZMV — 批次重新命名與搬移檔案

zmv 是 Zsh 內建的進階檔案搬移指令,支援 pattern matching。使用前需要先用 autoload -U zmv 載入。

例如要把所有 .log 檔案改成 .txt,可以用 capture group 語法:zmv '(*).log' '$1.txt'。對於簡單的副檔名替換,還可以用 -W flag 簡化語法。

安全功能方面,-n flag 提供 dry run 預覽模式,-i flag 啟用互動式確認模式,逐一詢問是否執行每個變更。zmv 對於不想寫腳本又需要批次管理檔案的人來說是一大利器。

[12:26] Named Directory — 目錄書籤

使用 hash -d 可以為常用目錄建立簡短的書籤名稱。例如 hash -d yt=~/youtube-projects,之後就能用 ~yt 在任何指令中引用這個目錄。

作者提到 named directory 在搭配特定 CLI 指令操作常用資料夾時很實用,但如果是單純的檔案系統導航,zoxide 仍然是更好的選擇。兩者可以互補使用。

[13:22] 自訂 Widget — 用 ZLE 打造個人化功能

Zsh widget 是一種專門用於操作終端機和命令列緩衝區的函數。透過 zle(Zsh Line Editor)指令可以定義自訂 widget。

作者分享了兩個實用範例:

  1. 清除歷史保留命令Ctrl+X L):清除終端畫面但保留當前正在輸入的指令,比 clear 指令更好用
  2. 複製命令到剪貼簿Ctrl+X Ctrl+C):將當前命令列的內容複製到系統剪貼簿

[14:30] Bindkey -s — 熱鍵插入指令樣板

最後一個技巧也是作者的最愛:使用 bindkey -s 設定熱鍵來插入預設的指令文字。例如綁定 Ctrl+X G C 來插入 git commit -m "",並透過 \C-b 語法將游標自動移到引號內,讓你可以直接開始輸入 commit message。

這個功能讓 Zsh 的操作體驗更接近 Emacs 或 Neovim 這類文字編輯器,可以為常用的指令樣板建立快速插入的動作,大幅減少重複輸入的時間。

我的想法

這部影片展示的技巧都不是什麼花俏的新工具,而是 Zsh 本身就內建的功能。這正好說明了一件事:大多數人只用到了 shell 不到 10% 的能力。特別是 zmvsuffix aliasbindkey -s 這三個功能,能直接減少日常開發中大量的重複操作。

chpwd hook 的三個應用場景非常值得參考。在 Vibe Coding 時代,開發者經常需要在多個專案之間切換,每個專案可能使用不同的 Python 版本、Node 版本或開發環境配置。用 hook 自動處理這些環境切換,比手動 activate/deactivate 省事許多,也能避免「忘記切換環境」導致的各種奇怪錯誤。

不過要注意的是,過度客製化 shell 環境也有風險:當你習慣了自己機器上的各種快捷鍵和自動化設定後,到了新環境(如 SSH 到遠端伺服器、pair programming 使用別人的電腦)可能會很不適應。建議將這些設定都版本控制在 dotfiles 倉庫中,方便在任何機器上快速部署。

進階測驗:10 個讓你相見恨晚的 Zsh 技巧

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在終端機中輸入一段很長的多行指令,突然發現第二行有個路徑打錯了。你想用最有效率的方式修正這個錯誤。應該怎麼做? 情境題

docker run -d \ –name my-app \ -v /home/usr/data:/app/data \ # ← 這裡 usr 應該是 user -p 8080:80 \ my-image:latest
  • A. 用方向鍵慢慢導航到錯誤位置,手動修改
  • B. 按 Ctrl+X Ctrl+E 在文字編輯器中開啟命令緩衝區,修改後儲存
  • C. 按 Ctrl+C 取消指令,重新輸入整段
  • D. 按 Ctrl+W 逐詞刪除到錯誤位置再重新輸入

2. 你的團隊有多個專案,分別使用 Node 16、18 和 20。每次切換專案目錄都要手動執行 nvm use 很麻煩。你想讓 Zsh 在切換目錄時自動偵測 .nvmrc 並切換 Node 版本。最適合的做法是? 情境題

  • A. 在 .zshrc 中設定一個全域的 Node 版本
  • B. 為每個專案建立 suffix alias,讓 .js 檔案自動用對應版本執行
  • C. 用 add-zsh-hook chpwd 掛載一個函數,偵測目錄中的 .nvmrc 後自動執行 nvm use
  • D. 用 global alias 定義 alias -g NV='nvm use',每次手動輸入 NV

3. 你有一個資料夾裡面有上百個 .jpeg 檔案,需要全部改成 .jpg 副檔名。你不想寫腳本,想用 Zsh 內建功能快速完成。最合適的做法是? 情境題

  • A. 用 for f in *.jpeg; do mv "$f" "${f%.jpeg}.jpg"; done 寫一個迴圈
  • B. 先 autoload -U zmv,然後執行 zmv -W '*.jpeg' '*.jpg'
  • C. 用 rename 's/.jpeg/.jpg/' *.jpeg 指令
  • D. 用 alias -s jpeg=mv 設定 suffix alias 後逐一重新命名

4. 小華在 .zshrc 中設定了以下 global alias,但使用時卻沒有生效,指令直接報錯。問題最可能出在哪裡? 錯誤診斷

# .zshrc 中的設定 alias -g ne=’2>/dev/null’ # 實際使用 $ du -sh /* ne du: cannot access ‘ne’: No such file or directory
  • A. Global alias 使用了小寫名稱 ne,與檔案名稱衝突;應該用大寫 NE 避免問題
  • B. 語法錯誤,alias -g 不支援重新導向符號 2>
  • C. 需要在 alias 後面加上分號 ; 才能正確展開
  • D. -g flag 只在 Bash 中有效,Zsh 不支援 global alias

5. 小明想在 Zsh 中為兩個不同的目錄事件設定自動化行為,但只有第二個 hook 生效。請問問題出在哪? 錯誤診斷

# .zshrc 中的設定 chpwd() { if [[ -f “flake.nix” ]]; then nix develop fi } chpwd() { if [[ -f “.nvmrc” ]]; then nvm use fi }
  • A. chpwd 不支援條件判斷,應該改用 precmd hook
  • B. Zsh 只能定義一個 chpwd 函數,第二個定義覆蓋了第一個;應該用 add-zsh-hook chpwd 分別掛載多個函數
  • C. 兩個 hook 函數名稱相同但邏輯衝突,需要加上 return 語句
  • D. chpwd hook 在 .zshrc 中不會被載入,需要放在 .zprofile
0

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *