哦!我的Zsh!

2014-03-27

Shell, Zsh, Tool

不久前,我从Bash切换到了Zsh,写点东西供读者以及我自己参考。

Zsh是什么

Zsh是一个Unix Shell,它在兼容标准的POSIX Shell(以及可仿真Bash)的同时,提供了极强的可定制性和可扩展性,以及一些有趣的功能,比如:

  • 自定义提示符,可以与git等软件集成;
  • 可编程的命令补全,例如输入kill命令后按tab会自动列出进程;
  • 全局可共享、并且能以各种方式管理的命令历史;
  • 命令补全错误纠正、界面主题包、不输入cd只输入目录名直接进入目录……

Zsh在使用时需要注意的独特之处包括:

  • 通配符展开是语言级的,通配符无法匹配是一个语法错误;
  • 一些命令是内建的,与其它Shell中的不同,例如echo命令在默认情况下会进行字符转义;
  • 从1开始数(第三声)数(第四声)。

顺带一提:Zsh取自Zhong Shao的登录名——此人是作者Paul Falstad的大学助教。为了名垂青史,请使用姓名拼音作为登录名,并耐心等待躺枪。我常常混淆Zsh和ZFS的各种梗,后者的“Z”代表Zettabyte和“终极”(作为最后一个字母)。

Oh-my-zsh

为了充分体验Zsh的强大之处,我们需要对它进行一些配置。

一个简单的方法是使用Oh-my-zsh——Oh-my-zsh是一个Zsh配置管理框架,自带上百个插件和主题。

直接下载并作为Shell脚本运行http://install.ohmyz.sh即可安装。

当然,你也可以选择从零开始进行配置。

配置

Zsh的配置文件位于~/.zshrc

从文件末尾开始添加配置。首先,我们可以根据自己的喜好,添加一些别名:

1 alias ll='ls -al'
2 alias la='ls -a'
3 alias apt-get='sudo apt-get'
4 alias gita='git add'
5 alias gitc='git commit'
6 ...

扩展名别名:

1 alias -s gz='tar -xzvf'
2 alias -s bz2='tar -xjvf'
3 alias -s out='valgrind'
4 ...

还可以加上快捷地址,比如hash -d www='/var/www/'

Zsh的提示符是可以修改的。我使用的版本:

1 ZSH_THEME_GIT_PROMPT_CLEAN=""
2 ZSH_THEME_GIT_PROMPT_DIRTY=" !"
3 ZSH_THEME_GIT_PROMPT_PREFIX=" :"
4 ZSH_THEME_GIT_PROMPT_SUFFIX=""
5 PROMPT=$(echo "${ret_status}%{$fg_bold[green]%}%p%{$fg[blue]%}%5~\
6 %{$fg_bold[cyan]%}$(git_prompt_info) %{$reset_color%}")

一些关于补全和错误纠正的配置:

1 zstyle ':completion:*' completer _complete _match _approximate
2 zstyle ':completion:*:match:*' original only
3 zstyle ':completion:*:approximate:*' max-errors 1 numeric
4 
5 zstyle ':completion:*:*:kill:*' menu yes select
6 zstyle ':completion:*:kill:*' force-list always
7 
8 zstyle ':completion:*:cd:*' ignore-parents parent pwd

扩展

以下是一些从网上收集的小程序,可以直接在.zshrc中使用。

对每个目录使用独立的命令历史:

 1 mkdir -p $HOME/.zsh_history$PWD
 2 export HISTFILE="$HOME/.zsh_history$PWD/zhistory"
 3 
 4 cd() {
 5     builtin cd "$@"
 6     fc -W
 7     local HISTDIR="$HOME/.zsh_history$PWD"
 8     if  [ ! -d "$HISTDIR" ] ; then
 9         mkdir -p "$HISTDIR"
10     fi
11     export HISTFILE="$HISTDIR/zhistory"
12     touch $HISTFILE
13     local ohistsize=$HISTSIZE
14     HISTSIZE=0
15     HISTSIZE=$ohistsize
16     fc -R
17 }
18 
19 function allhistory {
20     cat $(find $HOME/.zsh_history -name zhistory)
21 }
22 
23 function convhistory {
24     sort $1 | uniq |
25     sed 's/^:\([ 0-9]*\):[0-9]*;\(.*\)/\1::::::\2/' |
26     awk -F"::::::" '{ $1=strftime("%Y-%m-%d %T",$1) "|"; print }' 
27 }
28 
29 function histall {
30     convhistory =(allhistory) | sed '/^.\{20\} *cd/i\\'
31 }
32 
33 function hist {
34     convhistory $HISTFILE
35 }
36  
37 function top50 {
38     allhistory | awk -F':[ 0-9]*:[0-9]*;' '{ $1="" ; print }'\
39         | sed 's/ /\n/g' | sed '/^$/d' | sort | uniq -c | sort -nr | head -n 50
40 }

语法高亮:

 1 setopt extended_glob
 2 TOKENS_FOLLOWED_BY_COMMANDS=\
 3     ('|' '||' ';' '&' '&&' 'sudo' 'do' 'time' 'strace' 'man')
 4 
 5 recolor-cmd() {
 6     region_highlight=()
 7     colorize=true
 8     start_pos=0
 9     for arg in ${(z)BUFFER}; do
10         ((start_pos+=${#BUFFER[$start_pos+1,-1]}\
11             -${#${BUFFER[$start_pos+1,-1]## #}}))
12         ((end_pos=$start_pos+${#arg}))
13         if $colorize; then
14             colorize=false
15             res=$(LC_ALL=C builtin type $arg 2>/dev/null)
16             case $res in
17                 *'reserved word'*)   style="fg=magenta,bold";;
18                 *'alias for'*)       style="fg=cyan,bold";;
19                 *'shell builtin'*)   style="fg=yellow,bold";;
20                 *'shell function'*)  style='fg=green,bold';;
21                 *"$arg is"*)        
22                     [[ $arg = 'sudo' ]] && style="fg=red,bold"\
23                                   || style="fg=blue,bold";;
24                 *)                   style='none,bold';;
25             esac
26             region_highlight+=("$start_pos $end_pos $style")
27         fi
28         [[ ${${TOKENS_FOLLOWED_BY_COMMANDS[(r)${arg//|/\|}]}:+yes} = 'yes' ]]\
29             && colorize=true
30         start_pos=$end_pos
31     done
32 }
33 
34 check-cmd-self-insert() { zle .self-insert && recolor-cmd }
35 check-cmd-backward-delete-char() { zle .backward-delete-char && recolor-cmd }
36 
37 zle -N self-insert check-cmd-self-insert
38 zle -N backward-delete-char check-cmd-backward-delete-char

按两次esc键,快捷sudo:

1 sudo-command-line() {
2     [[ -z $BUFFER ]] && zle up-history
3     [[ $BUFFER != sudo\ * ]] && BUFFER="sudo $BUFFER"
4     zle end-of-line
5 }
6 zle -N sudo-command-line
7 bindkey "\e\e" sudo-command-line

当然,Zsh还可以变得更酷、更强。通过扩展和配置,我们可以让它做各种事情,尽管发挥你的想象力吧!