{% raw %}
## 第?30?章?終極 Shell -- ZSH
**目錄**
[](ch30.html#id3145843)
[Zsh 簡介](ch30s02.html)
[提示符](ch30s02.html#id3145224)
[自動補全](ch30s02.html#id3145250)
[路徑別名](ch30s02.html#id3145888)
[后綴別名](ch30s02.html#id3145943)
[鍵綁定](ch30s02.html#id3145982)
[錯誤校正](ch30s02.html#id3146009)
[Zsh 的強大特性](ch30s03.html)
[重定向功能](ch30s03.html#id3146087)
[補全類型控制](ch30s03.html#id3146129)
[計算器](ch30s03.html#id3146151)
[命令替換](ch30s03.html#id3146207)
[Zsh 配置文件](ch30s04.html)
## Zsh 簡介
zsh: The last shell you’ll ever need!
Z 是最后一個字母,所以它是終極 Shell。
zsh 擁有很多非常實用的功能
### 提示符
Zsh 的命令提示符令人印象深刻。它支持右側對齊的提示符,并且可以配置成這個樣子的:

### 自動補全
Zsh 的自動補全功能十分強大,可以根據上下文補全命令、選項、文件名、進程、用戶名、變量、權限符等。不需要記憶繁多的快捷鍵,只要按 `Tab` 就可以了。

### 路徑別名
Zsh 擁有貼心的路徑別名。假設有一個很長的路徑,例如 `/home/lighttpd/html`,可以把這個路徑命名為 `~WWW`。

也可以使用 **cd** 歷史紀錄,`cd -(TAB)`

### 后綴別名
zsh 還可以設定后綴別名
```
#alias 默認為命令別名
alias ls='ls -F --color=auto'
#使用選項 -s 定義后綴別名,鍵入擴展名為“xml”的文件名,自動使用 emacs 打開
alias `-s` xml=emacs
```
### 鍵綁定
Zsh 默認使用 Emacs風格 的鍵綁定,習慣 Bash 鍵綁定的朋友無需重新適應。Zsh 兼容大多數主流 Shell,像 Bash、Csh 等。
### 錯誤校正
Zsh 還擁有錯誤校正的功能

-- directory -- 是補全類型提示
/etc/x11 [tab] 后被修正為 /etc/X11 ,補全類型提示變成了 -- corrections --

這個功能不是單純的修正大小寫,而是各種拼寫錯誤。 比如說上面的例子,如果輸入的是 11 或者 s11,它一樣會修正為 X11
有一個前提,就是每次修正,只允許有一處字符錯誤。 兩個以上的錯誤,除非可以匹配其它的選項,否則就不能修正, 12 就不能修正為 X11 ,除非候選里有 X12、Y12、Z12……
在配置文件里找到這一行,修改容錯字數
```
zstyle ':completion:*:approximate:*' max-errors 1 numeric
```
## Zsh 的強大特性
### 重定向功能
示例:
| | |
| --- | --- |
| 重定向 stdout 和 stderr 到 file | command |& >file |
| 同時重定向到多個文件 | command >file.1 >file.2 |
### 補全類型控制
mpg123 [tab] , 候選菜單中只出現擴展名為 .mp3 .MP3 的文件:
```
zstyle ':completion:*:*:mpg123:*' file-patterns \
'*.(mp3|MP3):mp3\ files *(-/):directories'
zstyle ':completion:*:*:ogg123:*' file-patterns \
'*.(ogg|OGG):ogg\ files *(-/):directories'
```
### 計算器
zsh 可以當作計算器使用
```
#載入數學函數模塊 可以進行一些比較高級的運算
#(也可以將此句寫在配置文件中)
$ zmodload zsh/mathfunc
#`$((`數學表達式`))` 進行運算,使用 echo 顯示結果
$ echo $(( sin(1/4.0)**2 + cos(1/4.0)**2 - 1 ))
-1.1102230246251565e-16
$ echo $(( pi = 4.0 * atan(1.0) ))
3.1415926535897931
$ echo $(( f = sin(0.3) ))
0.29552020666133955
$ print $((1e12 * rand48()))
847909677310.23413
$ print $(( rand48(seed) ))
0.01043488334700271
```
### 命令替換
```
# bash 中使用這種形式
$ emacs find . -name "*.html"
# zsh 同樣支持,并可以使用以下形式
$ emacs `$(`ls **/*.html`)`
# zsh 還可以將命令結果生成臨時文件,并返回文件名
#支持更復雜的輸出和過濾。例如,比較 `new/` 和 `old/` 兩個文件夾的內容
$ diff `=(`ls new/`)` `=(`ls old/`)`
#查看生成的臨時文件
ls =()
```
## Zsh 配置文件
**例?30.1.?Zsh 配置文件 `.zshrc`**
```
#color{{{
autoload colors
colors
for color in RED GREEN YELLOW BLUE MAGENTA CYAN WHITE; do
eval _$color='%{$terminfo[bold]$fg[${(L)color}]%}'
eval $color='%{$fg[${(L)color}]%}'
(( count = $count + 1 ))
done
FINISH="%{$terminfo[sgr0]%}"
#}}}
#命令提示符 {{{
RPROMPT=$(echo "$RED%D %T$FINISH")
PROMPT=$(echo "$BLUE%M$GREEN%/
$CYAN%n $_YELLOW>>>$FINISH ")
#}}}
#標題欄、任務欄樣式{{{
case $TERM in (*xterm*|*rxvt*|(dt|k|E)term)
precmd () { print -Pn "\e]0;%n@%M//%/\a" }
preexec () { print -Pn "\e]0;%n@%M//%/\ $1\a" }
;;
esac
#}}}
#關于歷史紀錄的配置 {{{
#歷史紀錄條目數量
export HISTSIZE=10000
#注銷后保存的歷史紀錄條目數量
export SAVEHIST=10000
#歷史紀錄文件
#export HISTFILE=~/.zhistory
#以附加的方式寫入歷史紀錄
setopt INC_APPEND_HISTORY
#如果連續輸入的命令相同,歷史紀錄中只保留一個
setopt HIST_IGNORE_DUPS
#為歷史紀錄中的命令添加時間戳
setopt EXTENDED_HISTORY
#啟用 cd 命令的歷史紀錄,cd -[TAB]進入歷史路徑
setopt AUTO_PUSHD
#相同的歷史路徑只保留一個
setopt PUSHD_IGNORE_DUPS
#在命令前添加空格,不將此命令添加到紀錄文件中
#setopt HIST_IGNORE_SPACE
#}}}
#每個目錄使用獨立的歷史紀錄{{{
cd() {
builtin cd "$@" # do actual cd
fc -W # write current history file
local HISTDIR="$HOME/.zsh_history$PWD" # use nested folders for history
if [ ! -d "$HISTDIR" ] ; then # create folder if needed
mkdir -p "$HISTDIR"
fi
export HISTFILE="$HISTDIR/zhistory" # set new history file
touch $HISTFILE
local ohistsize=$HISTSIZE
HISTSIZE=0 # Discard previous dir's history
HISTSIZE=$ohistsize # Prepare for new dir's history
fc -R #read from current histfile
}
mkdir -p $HOME/.zsh_history$PWD
export HISTFILE="$HOME/.zsh_history$PWD/zhistory"
function allhistory { cat $(find $HOME/.zsh_history -name zhistory) }
function convhistory {
sort $1 | uniq |
sed 's/^:\([ 0-9]*\):[0-9]*;\(.*\)/\1::::::\2/' |
awk -F"::::::" '{ $1=strftime("%Y-%m-%d %T",$1) "|"; print }'
}
#使用 histall 命令查看全部歷史紀錄
function histall { convhistory =(allhistory) |
sed '/^.\{20\} *cd/i\\' }
#使用 hist 查看當前目錄歷史紀錄
function hist { convhistory $HISTFILE }
#全部歷史紀錄 top44
function top44 { allhistory | awk -F':[ 0-9]*:[0-9]*;' '{ $1="" ; print }' | sed 's/ /\n/g' | sed '/^$/d' | sort | uniq -c | sort -nr | head -n 44 }
#}}}
#雜項 {{{
#允許在交互模式中使用注釋 例如:
#cmd #這是注釋
setopt INTERACTIVE_COMMENTS
#啟用自動 cd,輸入目錄名回車進入目錄
#稍微有點混亂,不如 cd 補全實用
#setopt AUTO_CD
#擴展路徑
#/v/c/p/p => /var/cache/pacman/pkg
setopt complete_in_word
#禁用 core dumps
limit coredumpsize 0
#Emacs風格 鍵綁定
bindkey -e
#設置 [DEL]鍵 為向后刪除
bindkey "\e[3~" delete-char
#以下字符視為單詞的一部分
WORDCHARS='*?_-[]~=&;!#$%^(){}<>'
#}}}
#自動補全功能 {{{
setopt AUTO_LIST
setopt AUTO_MENU
#開啟此選項,補全時會直接選中菜單項
#setopt MENU_COMPLETE
autoload -U compinit
compinit
#自動補全緩存
#zstyle ':completion::complete:*' use-cache on
#zstyle ':completion::complete:*' cache-path .zcache
#zstyle ':completion:*:cd:*' ignore-parents parent pwd
#自動補全選項
zstyle ':completion:*' verbose yes
zstyle ':completion:*' menu select
zstyle ':completion:*:*:default' force-list always
zstyle ':completion:*' select-prompt '%SSelect: lines: %L matches: %M [%p]'
zstyle ':completion:*:match:*' original only
zstyle ':completion::prefix-1:*' completer _complete
zstyle ':completion:predict:*' completer _complete
zstyle ':completion:incremental:*' completer _complete _correct
zstyle ':completion:*' completer _complete _prefix _correct _prefix _match _approximate
#路徑補全
zstyle ':completion:*' expand 'yes'
zstyle ':completion:*' squeeze-shlashes 'yes'
zstyle ':completion::complete:*' '\\'
#彩色補全菜單
eval $(dircolors -b)
export ZLSCOLORS="${LS_COLORS}"
zmodload zsh/complist
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31'
#修正大小寫
zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}'
#錯誤校正
zstyle ':completion:*' completer _complete _match _approximate
zstyle ':completion:*:match:*' original only
zstyle ':completion:*:approximate:*' max-errors 1 numeric
#kill 命令補全
compdef pkill=kill
compdef pkill=killall
zstyle ':completion:*:*:kill:*' menu yes select
zstyle ':completion:*:*:*:*:processes' force-list always
zstyle ':completion:*:processes' command 'ps -au$USER'
#補全類型提示分組
zstyle ':completion:*:matches' group 'yes'
zstyle ':completion:*' group-name ''
zstyle ':completion:*:options' description 'yes'
zstyle ':completion:*:options' auto-description '%d'
zstyle ':completion:*:descriptions' format $'\e[01;33m -- %d --\e[0m'
zstyle ':completion:*:messages' format $'\e[01;35m -- %d --\e[0m'
zstyle ':completion:*:warnings' format $'\e[01;31m -- No Matches Found --\e[0m'
zstyle ':completion:*:corrections' format $'\e[01;32m -- %d (errors: %e) --\e[0m'
# cd ~ 補全順序
zstyle ':completion:*:-tilde-:*' group-order 'named-directories' 'path-directories' 'users' 'expand'
#}}}
##行編輯高亮模式 {{{
# Ctrl+@ 設置標記,標記和光標點之間為 region
zle_highlight=(region:bg=magenta #選中區域
special:bold #特殊字符
isearch:underline)#搜索時使用的關鍵字
#}}}
##空行(光標在行首)補全 "cd " {{{
user-complete(){
case $BUFFER in
"" ) # 空行填入 "cd "
BUFFER="cd "
zle end-of-line
zle expand-or-complete
;;
"cd --" ) # "cd --" 替換為 "cd +"
BUFFER="cd +"
zle end-of-line
zle expand-or-complete
;;
"cd +-" ) # "cd +-" 替換為 "cd -"
BUFFER="cd -"
zle end-of-line
zle expand-or-complete
;;
* )
zle expand-or-complete
;;
esac
}
zle -N user-complete
bindkey "\t" user-complete
#}}}
##在命令前插入 sudo {{{
#定義功能
sudo-command-line() {
[[ -z $BUFFER ]] && zle up-history
[[ $BUFFER != sudo\ * ]] && BUFFER="sudo $BUFFER"
zle end-of-line #光標移動到行末
}
zle -N sudo-command-line
#定義快捷鍵為: [Esc] [Esc]
bindkey "\e\e" sudo-command-line
#}}}
#命令別名 {{{
alias -g cp='cp -i'
alias -g mv='mv -i'
alias -g rm='rm -i'
alias -g ls='ls -F --color=auto'
alias -g ll='ls -l'
alias -g grep='grep --color=auto'
alias -g ee='emacsclient -t'
#[Esc][h] man 當前命令時,顯示簡短說明
alias run-help >&/dev/null && unalias run-help
autoload run-help
#歷史命令 top10
alias top10='print -l ${(o)history%% *} | uniq -c | sort -nr | head -n 10'
#}}}
#路徑別名 {{{
#進入相應的路徑時只要 cd ~xxx
hash -d WWW="/home/lighttpd/html"
hash -d ARCH="/mnt/arch"
hash -d PKG="/var/cache/pacman/pkg"
hash -d E="/etc/env.d"
hash -d C="/etc/conf.d"
hash -d I="/etc/rc.d"
hash -d X="/etc/X11"
hash -d BK="/home/r00t/config_bak"
#}}}
##for Emacs {{{
#在 Emacs終端 中使用 Zsh 的一些設置 不推薦在 Emacs 中使用它
if [[ "$TERM" == "dumb" ]]; then
setopt No_zle
PROMPT='%n@%M %/
>>'
alias ls='ls -F'
fi
#}}}
#{{{自定義補全
#補全 ping
zstyle ':completion:*:ping:*' hosts 192.168.128.1{38,} www.g.cn \
192.168.{1,0}.1{{7..9},}
#補全 ssh scp sftp 等
my_accounts=(
{r00t,root}@{192.168.1.1,192.168.0.1}
kardinal@linuxtoy.org
123@211.148.131.7
)
zstyle ':completion:*:my-accounts' users-hosts $my_accounts
#}}}
#{{{ F1 計算器
arith-eval-echo() {
LBUFFER="${LBUFFER}echo \$(( "
RBUFFER=" ))$RBUFFER"
}
zle -N arith-eval-echo
bindkey "^[[11~" arith-eval-echo
#}}}
####{{{
function timeconv { date -d @$1 +"%Y-%m-%d %T" }
# }}}
## END OF FILE #################################################################
# vim:filetype=zsh foldmethod=marker autoindent expandtab shiftwidth=4
```
這是一種效果超炫的提示符,把上面與提示符相關的配置語句注釋掉,加入下面代碼
```
#線框型提示符
function precmd {
local TERMWIDTH
(( TERMWIDTH = ${COLUMNS} - 1 ))
###
# 如果路徑太長,截短它
FILLBAR=""
PWDLEN=""
#統計 %~ 雙字節字符
local count_db_wth_char="$( echo ${^${(%):-%~}} | sed 's/\(.\)/\1\n/g' | grep -c \[\^\!-\~\] )"
local promptsize=${#${(%):---(%n@%m:%l)---()--}}
local pwdsize=${#${(%):-%~}}+$count_db_wth_char
if [[ "$promptsize + $pwdsize" -gt $TERMWIDTH ]]; then
((PWDLEN=$TERMWIDTH - $promptsize))
else
FILLBAR="\${(l.(($TERMWIDTH - ($promptsize + $pwdsize)))..${HBAR}.)}"
fi
###
# Get APM info.
#if which ibam > /dev/null; then
#APM_RESULT=`ibam --percentbattery`
#elif which apm > /dev/null; then
#APM_RESULT=`apm`
#fi
}
setopt extended_glob
preexec () {
if [[ "$TERM" == "screen" ]]; then
local CMD=${1[(wr)^(*=*|sudo|-*)]}
echo -n "\ek$CMD\e\\"
fi
}
setprompt () {
###
# Need this so the prompt will work.
setopt prompt_subst
###
# See if we can use colors.
autoload colors zsh/terminfo
if [[ "$terminfo[colors]" -ge 8 ]]; then
colors
fi
for color in RED GREEN YELLOW BLUE MAGENTA CYAN WHITE; do
eval $color='%{$terminfo[bold]$fg[${(L)color}]%}'
eval LIGHT_$color='%{$fg[${(L)color}]%}'
(( count = $count + 1 ))
done
NO_COLOUR="%{$terminfo[sgr0]%}"
###
# See if we can use extended characters to look nicer.
typeset -A altchar
set -A altchar ${(s..)terminfo[acsc]}
SET_CHARSET="%{$terminfo[enacs]%}"
SHIFT_IN="%{$terminfo[smacs]%}"
SHIFT_OUT="%{$terminfo[rmacs]%}"
HBAR=${altchar[q]:--}
#HBAR=" "
ULCORNER=${altchar[l]:--}
LLCORNER=${altchar[m]:--}
LRCORNER=${altchar[j]:--}
URCORNER=${altchar[k]:--}
###
# Decide if we need to set titlebar text.
case $TERM in
xterm*)
TITLEBAR=$'%{\e]0;%(!.-=*[ROOT]*=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\a%}'
;;
screen)
TITLEBAR=$'%{\e_screen \005 (\005t) | %(!.-=[ROOT]=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\e\\%}'
;;
*)
TITLEBAR=''
;;
esac
###
# Decide whether to set a screen title
if [[ "$TERM" == "screen" ]]; then
STITLE=$'%{\ekzsh\e\\%}'
else
STITLE=''
fi
###
# APM detection
#if which ibam > /dev/null; then
#APM='$RED${${APM_RESULT[(f)1]}[(w)-2]}%%(${${APM_RESULT[(f)3]}[(w)-1]})$LIGHT_BLUE:'
#elif which apm > /dev/null; then
#APM='$RED${APM_RESULT[(w)5,(w)6]/\% /%%}$LIGHT_BLUE:'
#else
APM=''
#fi
###
# Finally, the prompt.
PROMPT='$SET_CHARSET$STITLE${(e)TITLEBAR}\
$CYAN$SHIFT_IN$ULCORNER$BLUE$HBAR$SHIFT_OUT(\
$GREEN%(!.%SROOT%s.%n)$GREEN@%m:%l\
$BLUE)$SHIFT_IN$HBAR$CYAN$HBAR${(e)FILLBAR}$BLUE$HBAR$SHIFT_OUT(\
$MAGENTA%$PWDLEN<...<%~%<<\
$BLUE)$SHIFT_IN$HBAR$CYAN$URCORNER$SHIFT_OUT\
$CYAN$SHIFT_IN$LLCORNER$BLUE$HBAR$SHIFT_OUT(\
%(?..$LIGHT_RED%?$BLUE:)\
${(e)APM}$YELLOW%D{%H:%M}\
$LIGHT_BLUE:%(!.$RED.$WHITE)%#$BLUE)$SHIFT_IN$HBAR$SHIFT_OUT\
$CYAN$SHIFT_IN$HBAR$SHIFT_OUT\
$NO_COLOUR '
RPROMPT=' $CYAN$SHIFT_IN$HBAR$BLUE$HBAR$SHIFT_OUT\
($YELLOW%D{%a,%b%d}$BLUE)$SHIFT_IN$HBAR$CYAN$LRCORNER$SHIFT_OUT$NO_COLOUR'
PS2='$CYAN$SHIFT_IN$HBAR$SHIFT_OUT\
$BLUE$SHIFT_IN$HBAR$SHIFT_OUT(\
$LIGHT_GREEN%_$BLUE)$SHIFT_IN$HBAR$SHIFT_OUT\
$CYAN$SHIFT_IN$HBAR$SHIFT_OUT$NO_COLOUR '
}
setprompt
```
{% endraw %}
- 開源世界旅行手冊
- 授權
- 致謝
- 序言
- 更新紀錄
- 導讀
- 如何寫作科技文檔
- 部分?I.?氣候
- 第?1?章?GUI? CLI?
- 第?2?章?UNIX 縮寫風格
- 第?3?章?版本號的迷霧
- 第?4?章???Vim 還是 Emacs
- 第?5?章???DocBook 還是 TeX
- 第?6?章?完全用 Gnu/Linux 工作
- 第?7?章?病毒
- 第?8?章?磁盤 分區
- 第?9?章?文件系統
- 第?10?章???發行版介紹
- 第?11?章???編程語言
- 第?12?章?無根的根:無名師的 Unix 心傳
- 部分?II.?地理
- 第?13?章?基礎知識
- 第?14?章?命令系統
- 第?15?章?基本系統
- 第?16?章?軟件管理
- 第?17?章?核心工具集
- 第?18?章?編譯工具鏈
- 第?19?章?圖形界面
- 第?20?章?國際化
- 第?21?章???內核
- 第?22?章?Grub
- 第?23?章?服務器
- 第?24?章?Vim 編輯器
- 第?25?章?Emacs 入門
- 第?26?章?正則表達式
- 第?27?章?docbook 指南
- 第?28?章?Git 版本控制系統
- 第?29?章?ConTeXt 入門指南
- 部分?III.?景觀
- 第?30?章?終極 Shell -- ZSH
- 第?31?章?完美工作站 Archlinux
- 第?32?章?組織你的意念:Emacs org mode
- 第?33?章???Zsh+screen
- 第?34?章???gentoo stage3
- 第?35?章???硬件問題
- 第?36?章???網絡設置
- 第?37?章???自制 LiveCD
- 第?38?章?awesome
- 第?39?章?openbox 工作環境
- 第?40?章???Emacs muse
- 第?41?章???寫作工具鏈
- 第?42?章?使用 lftp
- 第?43?章???Firefox 使用技巧
- 第?44?章???FVWM
- 部分?IV.?地質
- 第?45?章?Unix
- 第?46?章???Gnu
- 第?47?章?軟件業自由之神——Richard Stallman
- 第?48?章?Linux
- 第?49?章?GNOME與KDE的戰爭
- 第?50?章???Vim Emacs
- 第?51?章???年代紀
- 第?52?章?我的選擇
- 第?53?章???補遺