终极Shell——Zsh

2008-08-05 Kardinal Posted in Apps, CLIRSSTrackback

[撰文/Kardinal]

有句话这样说,zsh: The last shell you’ll ever need! Z是最后一个字母,所以是终极Shell。

我曾经搜索到一个比较各种Shell的文章,Zsh交互性是A+级别的,远高于其它Shell。在编程方面,Zsh是A级的吧,也是最高的。只是不知道出于什么原因,Zsh被严重的低估了。

大多数的Linux用户比较偏爱Bash,因为大多数的发行版默认的就是它。凭心而论,Bash确实比Csh之流的好用多了。不过Bash也有很多地方不尽人意,像自动补全的功能不够强大,定位较长路径不够方便等。

后来我使用Zsh。如果不调整一些必要的配置的话,Zsh甚至还不如Bash好用。这也是很多人尝试过并放弃过的原因。

不熟悉Zsh的人,对Zsh最深刻的印象应该就是它的命令提示符了。它支持右侧对齐的提示符,并且可以配置成这个样子的:

zsh1-thumb.gif

不过我还是喜欢比较简单的样式。

Zsh的自动补全功能十分的强大,如图所示:

zsh2-thumb.gif

它可以自动补全命令、参数、文件名、进程、用户名、变量、权限符等。

Zsh还有一个贴心的功能:路径别名。假设有一个很长的路径,例如/home/lighttpd/html,可以把这个路径命名为 ~WWW。

zsh3-thumb.gif

Zsh可以使用Emacs风格的键绑定,习惯Bash键绑定的朋友无需重新适应。Zsh兼容大多数主流Shell,像Bash、Csh等。

下面讲解一下Zsh配置文件:

#命令提示符 ( ^[ 是一个特殊字符 在vi插入模式下 按Ctrl+v ESC 插入该字符)
PROMPT='%{ ^[[36m%}%n%{^[[35m%}@%{^[[34m%}%M %{^[[33m%}%D %T %{^[[32m%}%/
%{^[[31m%}>>%{^[[m%}'

#关于历史纪录的配置
# number of lines kept in history
export HISTSIZE=10000
# # number of lines saved in the history after logout
export SAVEHIST=10000
# # location of history
export HISTFILE=~/.zhistory
# # append command to history file once executed
setopt INC_APPEND_HISTORY

#Disable 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

# Completion caching
zstyle ':completion::complete:*' use-cache on
zstyle ':completion::complete:*' cache-path .zcache
#zstyle ':completion:*:cd:*' ignore-parents parent pwd

#Completion Options
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

# Path Expansion
zstyle ':completion:*' expand 'yes'
zstyle ':completion:*' squeeze-shlashes 'yes'
zstyle ':completion::complete:*' '\\'

zstyle ':completion:*:*:*:default' menu yes select
zstyle ':completion:*:*:default' force-list always

# GNU Colors 需要/etc/DIR_COLORS文件 否则自动补全时候选菜单中的选项不能彩色显示
[ -f /etc/DIR_COLORS ] && eval $(dircolors -b /etc/DIR_COLORS)
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:*' completer _complete _match _approximate
zstyle ':completion:*:match:*' original only
zstyle ':completion:*:approximate:*' max-errors 1 numeric

compdef pkill=kill
compdef pkill=killall
zstyle ':completion:*:*:kill:*' menu yes select
zstyle ':completion:*:processes' command 'ps -au$USER'

# Group matches and Describe
zstyle ':completion:*:matches' group 'yes'
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'

#命令别名
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
alias ls='ls -F --color=auto'
alias ll='ls -l'
alias grep='grep --color=auto'
alias ee='emacsclient -t'

#路径别名 进入相应的路径时只要 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

效果超炫的提示符:

#效果超炫的提示符

function precmd {

local TERMWIDTH
(( TERMWIDTH = ${COLUMNS} - 1 ))

###
# Truncate the path if it's too long.

PR_FILLBAR=""
PR_PWDLEN=""

local promptsize=${#${(%):---(%n@%m:%l)---()--}}
local pwdsize=${#${(%):-%~}}

if [[ "$promptsize + $pwdsize" -gt $TERMWIDTH ]]; then
((PR_PWDLEN=$TERMWIDTH - $promptsize))
else
PR_FILLBAR="\${(l.(($TERMWIDTH - ($promptsize + $pwdsize)))..${PR_HBAR}.)}"
fi

###
# Get APM info.

#if which ibam > /dev/null; then
#PR_APM_RESULT=`ibam --percentbattery`
#elif which apm > /dev/null; then
#PR_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 PR_$color='%{$terminfo[bold]$fg[${(L)color}]%}'
eval PR_LIGHT_$color='%{$fg[${(L)color}]%}'
(( count = $count + 1 ))
done
PR_NO_COLOUR="%{$terminfo[sgr0]%}"

###
# See if we can use extended characters to look nicer.

typeset -A altchar
set -A altchar ${(s..)terminfo[acsc]}
PR_SET_CHARSET="%{$terminfo[enacs]%}"
PR_SHIFT_IN="%{$terminfo[smacs]%}"
PR_SHIFT_OUT="%{$terminfo[rmacs]%}"
PR_HBAR=${altchar[q]:--}
#PR_HBAR=" "
PR_ULCORNER=${altchar[l]:--}
PR_LLCORNER=${altchar[m]:--}
PR_LRCORNER=${altchar[j]:--}
PR_URCORNER=${altchar[k]:--}

###
# Decide if we need to set titlebar text.

case $TERM in
xterm*)
PR_TITLEBAR=$'%{\e]0;%(!.-=*[ROOT]*=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\a%}'
;;
screen)
PR_TITLEBAR=$'%{\e_screen \005 (\005t) | %(!.-=[ROOT]=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\e\\%}'
;;
*)
PR_TITLEBAR=''
;;
esac

###
# Decide whether to set a screen title
if [[ "$TERM" == "screen" ]]; then
PR_STITLE=$'%{\ekzsh\e\\%}'
else
PR_STITLE=''
fi

###
# APM detection

#if which ibam > /dev/null; then
#PR_APM='$PR_RED${${PR_APM_RESULT[(f)1]}[(w)-2]}%%(${${PR_APM_RESULT[(f)3]}[(w)-1]})$PR_LIGHT_BLUE:'
#elif which apm > /dev/null; then
#PR_APM='$PR_RED${PR_APM_RESULT[(w)5,(w)6]/\% /%%}$PR_LIGHT_BLUE:'
#else
PR_APM=''
#fi

###
# Finally, the prompt.

PROMPT='$PR_SET_CHARSET$PR_STITLE${(e)PR_TITLEBAR}\
$PR_CYAN$PR_SHIFT_IN$PR_ULCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
$PR_GREEN%(!.%SROOT%s.%n)$PR_GREEN@%m:%l\
$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_CYAN$PR_HBAR${(e)PR_FILLBAR}$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
$PR_MAGENTA%$PR_PWDLEN<...<%~%<<\
$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_CYAN$PR_URCORNER$PR_SHIFT_OUT\

$PR_CYAN$PR_SHIFT_IN$PR_LLCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
%(?..$PR_LIGHT_RED%?$PR_BLUE:)\
${(e)PR_APM}$PR_YELLOW%D{%H:%M}\
$PR_LIGHT_BLUE:%(!.$PR_RED.$PR_WHITE)%#$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_NO_COLOUR '

RPROMPT=' $PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_BLUE$PR_HBAR$PR_SHIFT_OUT\
($PR_YELLOW%D{%a,%b%d}$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_CYAN$PR_LRCORNER$PR_SHIFT_OUT$PR_NO_COLOUR'

PS2='$PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_BLUE$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT(\
$PR_LIGHT_GREEN%_$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT$PR_NO_COLOUR '
}

setprompt

Zsh还有一些功能,也是比较实用的:

错误校正

crct1.jpg

-- directory -- 是补全类型提示

/etc/x11 [tab] 后被修正为 /etc/X11。

补全类型提示变成了 -- corrections --

crct2.jpg

不是单纯的修正大小写,而是各种拼写错误。比如说上面的例子,如果输入的是11或者s11,它一样会修正为X11。

有一个前提,就是每次修正,只允许有一处字符错误。两个以上的错误,除非可以匹配其它的选项,否则就不能修正
12 就不能修正为X11 ,除非候选里有 X12、Y12、Z12……

在配置文件里找到这一行,修改容错字数:

zstyle ':completion:*:approximate:*' max-errors 1 numeric

当然可以把容错字数改大一些,不过太大了也没有意义了。随便输点什么,就可以匹配所有的,和没有一样。

强大的重定向功能

同时重定向stdout和stderr到file: command |& >file

同时重定向到多个文件: command >file.1 >file.2

比如装系统的时候,可以用这个命令:

blkid >> /boot/grub/menu.lst >> /etc/fstab

补全类型控制

例如:

compctl -g '*.tar.gz *.gz*.tgz' + -g '*(-/)' tar zxvf

过滤候选项

tar zxvf [tab]

候选菜单中只出现扩展名为 .tar.gz .gz .tgz 的文件。

不过这个功能比较复杂,容易引起混乱,通常需要脚本配合


compctl -g '*.tar.bz2 *.tar.gz *.bz2 *.gz *.jar *.rar *.tar *.tbz2 *.tgz *.zip *.Z' + -g '*(-/)' extract

extract() {
if [[ -z "$1" ]] ; then
print -P "usage: \e[1;36mextract\e[1;0m < filename >"
print -P " Extract the file specified based on the extension"
elif [[ -f $1 ]] ; then
case ${(L)1} in
*.tar.bz2) tar -jxvf $1 ;;
*.tar.gz) tar -zxvf $1 ;;
*.bz2) bunzip2 $1 ;;
*.gz) gunzip $1 ;;
*.jar) unzip $1 ;;
*.rar) unrar x $1 ;;
*.tar) tar -xvf $1 ;;
*.tbz2) tar -jxvf $1 ;;
*.tgz) tar -zxvf $1 ;;
*.zip) unzip $1 ;;
*.Z) uncompress $1 ;;
*) echo "Unable to extract '$1' :: Unknown extension"
esac
else
echo "File ('$1') does not exist!"
fi
}

考虑到使用的不多,配置又麻烦,我没有配置这个功能。不过我想肯定有人愿意在这上面花点时间。

将Zsh设置为默认Shell(不建议更改root用户的默认shell)

usermod -s /usr/local/bin/zsh

版权声明: 允许非商业性转载,但转载时必须标明作者及原文链接.
本文网址: http://linuxtoy.org/archives/zsh.html

Tags: ,

21 Comments
  1. 1 aqueura Commented @ 2008-08-05 8:18 am

    我一直都用zsh,但是用的很肤浅,受教了。

  2. 2 cabrio Commented @ 2008-08-05 8:50 am

    我一直想用,但是不知道如何将我现在的用户从bash变成zsh,我只会在新建用户时指定shell

  3. 3 oomengnan Commented @ 2008-08-05 8:55 am

    貌似很复杂
    菜鸟我还是用bash比较顺手

  4. 4 tdsparrow Commented @ 2008-08-05 9:00 am

    toy用的什么terminal呀,urxvt?一直想要有这种多window的效果.

  5. 5 aqueura Commented @ 2008-08-05 9:07 am

    @cabrio chsh 可以做到。

  6. 6 walkerxk Commented @ 2008-08-05 9:12 am

    右侧对齐的提示符没有什么意思,
    bash可以补全命令、参数、文件名、用户名
    那个路径别名一直在bash下使用,输入~然后按tab。

  7. 7 anton Commented @ 2008-08-05 9:18 am

    1. chsh 可以更換 login shell.
    2. /etc/DIR_COLORS 可以不用存在啊~ 參考 dircolors 去 export LS_COLORS 就好了。
    3. 一個可以直接 dl 下來的 .zshrc 試用好像比較方便點。
    4. zsh "好像" 跟 screen 衝突... Orz 我再調整看看~ 因為我非要 screen 不可。

  8. 8 tdsparrow Commented @ 2008-08-05 9:18 am

    原来答案就在下一篇,呵呵。原来看见这种平铺式的布局出现在mrxvt的todo list中,一直很期待,甚至都想contribute一下,好像现在都还没有release。

  9. 9 Jason Lee Commented @ 2008-08-05 9:20 am

    修改/etc/passwd文件就可以修改默认的SHELL了.

  10. 10 雪梨 Commented @ 2008-08-05 9:24 am

    bash 的全能补全也是被低估了吧

  11. 11 cabrio Commented @ 2008-08-05 9:50 am

    @aqueura
    @anton
    谢谢,回去马上试试

  12. 12 kardinal Commented @ 2008-08-05 9:55 am

    to:10 雪梨

    BASH也可以补全变量啊用户啊什么的,但是要按特殊的组合键,看过王垠那篇火星文的都知道
    ZSH会自动根据上下文补全,所有可能出现的东西

    bash_completion我也不是没有用过(用的时间挺长的应该说)
    如果够好了,我为什么要用ZSH

    仔细看看关于Zsh的讨论部分,常见的质疑都有回答:D
    http://forum.ubuntu.org.cn/viewtopic.php?t=138936&postdays=0&postorder=asc&start=15

  13. 13 Commented @ 2008-08-05 10:36 am

    to:walkerxk
    右侧对齐的提示符本身并不实用,它只是提供了一种可能。例如能将提示符配置成第一幅图中的下面那种
    bash可以补全命令、参数、文件名、用户名,这个看过王垠那篇火星文的都知道,但是要按特殊的组合键
    bash的路径别名还真不知道,以前用bash的时候搜遍大江南北也没找到这个功能:D

    to:anton
    配置文件整理后上传
    Ubuntu论坛上的roylez兄同时使用zsh和screen,他提供了一个解决方案
    http://forum.ubuntu.org.cn/viewtopic.php?t=138936&postdays=0&postorder=asc&start=15

  14. 14 anton Commented @ 2008-08-05 11:05 am

    谢谢 kardinal ,啧啧啧,这连结里附的影片 screen 跟 zsh 搭的真漂亮。
    真想跟他要 screenrc 跟 .zshrc/.zprofile 来玩。

    roylez 真是行~

  15. 15 K Commented @ 2008-08-05 11:39 am

    zsh 能不能像 fish 一样实现彩色的命令行提示?

    之前想换掉 bash,一直也听说 zsh 比较强大,可是实在要花些时间研究,所以就用 fish 去了。总的来说还是不错,我自己也没什么很高深的应用需求,fish 基本都没能满足了。但是有几个实在无法忍受的 bug,比如非 X 环境一登录就卡死……

  16. 16 itroad8 Commented @ 2008-08-05 11:47 am

    不如来个比较bash,cshell,zshell的文章吧

  17. 17 Kardinal Commented @ 2008-08-05 12:04 pm

    to:k
    应该是可以的,不过配置可能比较麻烦。google一下说不定能找到答案
    其实颜色太多也不好,gentoo的emerge开始觉得挺不错,色彩比较丰富,就是有点晃眼……最近又用archlinux,觉得不用彩色显示更好,可以看自己想看的信息,而不是被强迫看作者认为重要并彩色标示出来的信息

    fish运行起来感觉还是比较迟钝的,而zsh的速度感觉比bash还要快,哪怕是比较大的配置文件:D

    to:itroad8
    有比较各种shell的文章,但每个人关注的侧重点不一样。大的方面比较,结论在文章的开头给出了:
    Zsh交互性是A+级别的,远高于其它Shell。在编程方面,Zsh是A级的吧,也是最高的。只是不知道出于什么原因,Zsh被严重的低估了。

  18. 18 Kardinal Commented @ 2008-08-05 12:07 pm

    to:anton
    本文介绍的功能,基本上是每个人都需要的功能。无论怎么配置zsh,这些功能都是不可少的
    至于其它功能,自己想办法吧:D

  19. 19 Jan Commented @ 2008-08-05 12:10 pm

    zsh的completion比bash的强大太多,bash+bash_completion我以前也用过很长时间

    那个花里胡哨的提示符看久了有点儿腻,而且如果是screen+zsh的用户有些信息(比如时间)会和screen的提示栏重复

  20. 20 K Commented @ 2008-08-05 12:46 pm

    Criteria Nb sh ksh bash zsh csh tcsh
    Configurability 1 - + ++ +++ + ++
    Execution of commands 2 + + + ++ + ++
    Completion 3 -- + ++ +++ + ++
    Line editing 4 - + ++ ++ - ++
    Name substitution 5 + + ++ ++ + ++
    History 6 -- + ++ ++ + ++
    Redirections and pipes 7 + + + ++ + +
    Spelling correction 8 -- -- -- + -- +
    Prompt settings 9 + + + ++ + ++
    Job control 10 -- + + + + +
    Execution control 11 + + + + + +
    Signal Handling 12 + + + + - -

    来源:
    http://www.hep.phy.cam.ac.uk/lhcb/LHCbSoftTraining/documents/ShellChoice.pdf

  21. 21 希罗 Commented @ 2008-08-05 1:48 pm

    其实我是被zsh里更好的补全功能而吸引的!