bash用久了,有些地方開始覺得不爽,於是想看看有沒有更好的選擇。原來在網上瞎逛時,已經很多次看到有人推薦zsh了,加上zsh高度兼容bash,於是就來折騰這個。
不過試驗了一下oh-my-zsh,感覺功能太強大了,太多東西不知道怎么配置的,這種過於“不知其所以然”的感覺我不太喜歡。於是自己來折騰,覺得基本夠自己用就行了。
1. 補全
很多介紹zsh的文章都說zsh相對bash的一個優點是支持命令選項和參數的補全
(這兩幅圖來自 Z使用 Zsh 的九個理由 - 博客 - 伯樂在線)
其實bash里面如果你安裝了bash-completion這個包的話,很多命令(比如pkill, dpkg, git等等,我的機器上/etc/bash_completion.d/
下面有200多個命令的補全配置。注意/etc/bash_completion.d/git
其實是git包提供的,而/etc/bash_completion.d/mercurial
其實是mercurial包提供的) 。
不過zsh在補全還是有些比bash強的地方,尤其是涉及到交互的地方:
- 按TAB補全時,能夠在多個備選項之間循環,如果備選項不多的話,你只需要不斷按TAB就行了,而bash只會列出備選項,你得多輸入一個或幾個字母直到備選項縮小到一個了它才真正給你補全;
- 有一定的容錯能力: 可以在配置文件中添加一句
zstyle ':completion::approximate:' max-errors 1 numeric
,以后輸入cd /etc/x11, 按TAB后zsh會給你糾正為/etc/X11 (此條來自 終極Shell——Zsh — LinuxTOY,那里有詳細的說明); - 補全時可以用光標鍵或者Ctrl-p/Ctrl-n來挑選被選項(即很多文章說到的menu select方式)。不過針對第這一點,就得說到zsh的兩個缺點:配置太TMD復雜、說明文檔也巨羅嗦,你要是對缺省配置有一點點不滿意,或者搞錯了, 就很容易陷入泥沼里,有興趣的自己研究這個文檔吧: Chapter 6: Completion, old and new - A User's Guide to the Z-Shell (P.S. 一篇簡單的介紹: Refining Linux: ZSH Gem #5: Menu selection )
另外,zsh自帶的補全源的確比bash-completion多,我這里find /usr/share/zsh/functions/Completion -type f | wc -l
的結果是688個文件(參見 Debian -- Filelist of package zsh-common/jessie/all )
2. 在目錄中穿梭(cd命令)
在寫代碼過程中,會在各個目錄之間來回切換,原來用bash時有兩點最不爽:
- 對cd命令有TAB補全的功能,但每一級目錄都需要按TAB(並且有多個備選項的話需要繼續輸入才行)還是覺得繁瑣,覺得效率不高;
pushd/popd/dirs
雖然很有用,但很多時候等你想回到過去的某個目錄時,才發現當時忘記pushd了
2.1 zsh的改進方法
- 首先,假如
/opt/rubystack-1.9/
下面有apache2
和apps
這兩個目錄,輸入cd /opt/rubystack-1.9/a
然后按TAB的話,首先會補齊為apache2,再按TAB會補齊為apps,不需要象bash下面那樣繼續輸入字母; - 如果你想進入
/opt/rubystack-1.9/apps/redmine
,那么可以先這樣輸入cd /o/r/a/r
然后按TAB
,如果這是唯一匹配,那么zsh會補全為/opt/rubystack-1.9/apps/redmine,但如果還存在一個/opt/rubystack-1.8/apps/redmine,那zsh就會列出來讓你挑選;
- 如果你現在在
/opt/rubystack-1.9/apps/redmine,
但你想進入/opt/rubystack-1.8/apps/redmine,可以這樣: cd 1.9 1.8 這表示將完整路徑上的1.9替換為1.8再使用;
- 你可以打開
auto_pushd
選項(通過命令setopt auto_pushd
),這樣你通過cd切換目錄時,zsh會自動將前一個目錄加到棧里,這樣你就不會因為忘記pushd而遺憾了; - bash里面可以
cd -
回到上一個目錄(即最后一次調用cd時所在的目錄),但zsh里面有cd -2, cd +3這樣的用法,並且在輸入cd -之后按TAB能夠列出目錄名供挑選補全。不過需要注意的是,這里-2並不表示倒數第二次調用cd時的目錄,而是倒數第二次通過pushd記錄的目錄,如 果打開了auto_pushd選項,那么這兩個的含義倒是一樣的;
- zsh里面將~這個符號的用法進行了擴展,我們可以用
hash -d www=/var/www/html
定義一個路徑別名,然后用cd ~www
就可以進入到/var/www/html
了
參考資料: Refining Linux: ZSH Gem #20: Changing directories the pro's way
2.2 autojump: 快速進入頻繁訪問的目錄(bash/zsh通用)
使用方法
- 下載這個 https://github.com/rupa/z/blob/master/z.sh (這個文件名跟zsh沒有必然聯系),放到某個位置(比如
/usr/local/lib/z.sh
); - 在
~/.bashrc
或者~/.zshrc
里面加入一句source /usr/local/lib/z.sh
,這使得以后的cd命令會被z.sh統計各目錄訪問頻率; - 你得按老方法切換目錄一陣,以便z.sh能夠知道哪些目錄是最常用的;
- 用
z -l
命令即可列出歷史上你訪問各個目錄的頻率了; - 用
z
regex 命令即可進入你頻繁訪問的目錄
3. 提示符設置
oh-my-zsh提供了很多很花哨且功能強大的提示符設置,比如顯示git當前分支名、提交狀態等等:
3.1 基本設置
不過我覺得Debian缺省推薦的(見/etc/zsh/newuser.zshrc.recommended
)提示符設置就可以了(只是簡單地顯示用戶名、機器名和全路徑)
autoload -Uz promptinit promptinit prompt adam1
3.2 RPROMPT
不過我有時也想顯示一點別的內容,zsh有個RPROMPT環境變量用來設置顯示在右邊的提示符,可以在有需要的時候即設即用,很方便。比如我用它配置了 用來顯示當前git倉庫名、分支名和提交狀態(也許你又要問我為什么不用oh-my-zsh,那是因為我沒看見它顯示git倉庫名,而這個龐大的東西我不 知道怎么去修改它。甚至下面這段代碼我也是拋棄了StackOverflow上得票最多的答案,而是選了個得分為0的答案為基礎,僅僅因為后面這個我能看懂,能修改它)
_git_repo_name() { gittopdir=$(git rev-parse --git-dir 2> /dev/null) if [[ "foo$gittopdir" == "foo.git" ]]; then echo `basename $(pwd)` elif [[ "foo$gittopdir" != "foo" ]]; then echo `dirname $gittopdir | xargs basename` fi } _git_branch_name() { git branch 2>/dev/null | awk '/^\*/ { print $2 }' } _git_is_dirty() { git diff --quiet 2> /dev/null || echo '*' } setopt prompt_subst RPROMPT='$(_git_repo_name) $(_git_branch_name) $(_git_is_dirty)'
4. 命令歷史
4.1 多會話共享歷史
我一般會用tmux
開多個會話,這種情況下記得剛才輸入過這個命令,但找了半天沒找到才意識到好像是在另外一個window/pane里面輸入的。zsh的一個功能特性是share_history
,這樣在一個會話里面可以訪問另外一個會話的歷史命令了。不過這個特性可能會讓人有些不習慣,這樣的話可以試試另外兩個選項:setopt APPEND_HISTORY或者setopt INC_APPEND_HISTORY.
參考資料: Refining Linux: ZSH Gem #15: Shared history
4.2 a clear history
setopt hist_ignore_space alias cd=" cd" alias ls=" ls"
第一句使得不將以空格開始的命令行記錄到歷史當中(這在需要在i命令行中明文輸入密碼時也挺有用,這樣不會在歷史記錄中看到你的密碼了);
后面兩句使得cd/ls這些簡單的命令就不記錄到歷史了,這樣用history查看的時候是不是更清晰了?
來自 : http://chneukirchen.org/blog/archive/2012/02/10-new-zsh-tricks-you-may-not-know.html
另一個技巧: 每個目錄記錄自己的history: <http://linuxtoy.org/archives/zsh_per_dir_hist.html>
http://www.lowlevelmanager.com/2012/05/zsh-history-expansion.html
5. 其它設置
5.1 通配符帶子目錄
zsh支持更復雜的文件名匹配,不過我大部分我都沒去學,只記住了我認為最有用的一個:
grep ":project_menu" **/*.erb
這個**會搜索所有的子目錄,也就避免了用find了(bash下就得借用find了: find . -name '*.erb | xargs grep ":project_menu")
5.2 setopt no_nomatch
這是zsh缺省跟bash不兼容的一個地方。在zsh下,如果你執行dpkg -l firefox*
,很可能zsh不會列出名字以firefox開頭的包,而是告訴你zsh: no matches found: firefox*
。這是因為zsh缺省情況下始終自己解釋這個firefox*,而不會傳遞給dpkg來解釋。
解決這個問題的方法是在~/.zshrc中加入:
setopt no_nomatch
5.3 ~/.inputrc無法工作?
zsh沒有使用libreadline,而是自己實現了一套類似的(名字叫做zle - zsh command line editor)。所以你原來在~/.inputrc里面配置的快捷鍵都不再有效,而是要用zle的語法重新配置一遍(也在~/.zshrc中配置)。
例子1:
bindkey "^[0H" beginning-of-line bindkey "^[OF" end-of-line
例子2:
#按下F5時自動輸入z -l | tail\n bindkey -s "\e[15~" "z -l | tail\n"
5.4 讓M-backspace象bash那樣只回刪一個單詞
這是zsh跟bash不兼容的另一個地方,M-backspace會回刪整個參數,而不是像bash/emacs那樣只刪除一個單詞。
解決方法是在~/.zshrc中加入:
autoload -U select-word-style select-word-style bash
(來自: http://stackoverflow.com/a/1438523 )
6. 參考資料
- [系統管理員工具包: 充分利用 zsh](http://www.ibm.com/developerworks/cn/aix/library/au-satzsh.html)
- [幕啟:介紹 Z shell](http://www.ibm.com/developerworks/cn/linux/shell/z/)
- [終極Shell——Zsh — LinuxTOY](http://linuxtoy.org/archives/zsh.html)
- [使用 Zsh 的九個理由 - 博客 - 伯樂在線](http://blog.jobbole.com/28829/)