zsh

来自百合仙子's Wiki
跳转到导航 跳转到搜索
本页主题是zsh的交互使用,关于zsh下的shell编程,见 zsh编程

TODO

按文档清理

命令行编辑

bindkey命令用于设置键绑定。

控制字符

M-数字
重复后续命令 N 次,重复则表示多位数
M-q
暂时离开当前命令的编辑,执行其它命令。 ^Q 也绑定到这里,但没有作用
M-. M-_
循环 yank 最近命令的最后一项
^Xu ^X^U ^_ undo
撤消
^V quoted-insert
按本义插入字符
^X^K kill-buffer
删除整个命令
^Xs history-incremental-search-forward
向后搜索历史。默认 ^S 也绑定到这里,但没有作用
^X^F 自定义 ^] vi-find-next-char
向后搜索字符
(未绑定) 自定义 M-] vi-find-prev-char
向前搜索字符
M-^_ copy-prev-word
复制前一个单词
^X^B vi-match-bracket
跳到对应的括号
M-' quote-line
用单引号括起整行
M-? which-command
查询当前命令的位置
^X^O overwrite-mode
插入/替换
M-x execute-named-cmd
按命令名执行一条编辑命令
M-z execute-last-named-cmd
执行上一条编辑命令
(未绑定) where-is
查询命令被绑定到什么键上去了

历史记录

M-< beginning-of-buffer-or-history
取出历史记录的第一条命令
M-> end-of-buffer-or-history
取出历史记录的最后一条命令
^o accept-line-and-down-history
执行本行命令并取出历史记录中的下一项

补全和扩展

这些内容在手册的 Completion System -> Bindable Commands 下。

^Xa _expand_alias
作为别名扩展
M-/ _history-complete-older
从历史记录的单词中寻找补全
(未绑定) 自定义 M-/ _history-complete-older
从历史记录的单词中寻找旧的补全
^Xg list-expand
列表扩展结果,但不进行实际的扩展。在文件名生成时,总是按文件名排序。

=cmd 可扩展成命令 cmd 的绝对路径(即 which cmd )。

其它

^a
使用上行命令,删除第一个 a
^a^b
使用上行命令,其中的第一个 a 被 b 取代
^X^V vi-cmd-mode
原地切换到vi命令模式,按 i 键后回到Emacs模式

设置

使用 $EDITOR 编辑命令行

autoload edit-command-line
zle -N edit-command-line
bindkey '^X^E' edit-command-line

这种方法不适用于内建命令vared

bashbackward-kill-word

autoload -U select-word-style
select-word-style bash

来源:stackoverflow: zsh: stop backward-kill-word on directory delimiter

URL 中的特殊字符自动转义:

autoload -U url-quote-magic
zle -N self-insert url-quote-magic

提示符

函数 precmd 在显示提示符之前执行,可以在此时修改提示符或者打印消息。

内建命令

alias

后缀别名,在命令行上指定一个文件,当其后缀符合时使用该命令打开。文件不可使用引号括起。

alias -s jar="java -jar"

设置全局别名,该别名在命令行中任意位置单独作为一个单词时将被替换。

alias -g NUL="/dev/null"

bindkey

显示所有绑定:

bindkey -L

绑定到一个功能:

bindkey "^U" backward-kill-line

绑定到一个键序列:

bindkey -s "^J" "^[[A^[[B"

vared

交互式编辑变量的值。

vared VAR

特殊函数

command_not_found_handler
命令未找到时执行
zshexit
退出时执行

命令行选项

-x
启动时打印执行的命令

文件

作为 login shell 时会读取 ~/.zprofile

选项

setopt 置位一个选项, unsetopt 复位一个选项。不加参数的 setopt 显示所有已置位的选项,不加参数 unsetopt 显示所有已复位的选项。选项名不区分大小写。

# append command to history file once executed
setopt inc_append_history
extended_glob
启用 (#qx) 式 glob
sh_word_split
bash那样扩展变量后进行分割(否则使用单引号括起来)。也可以使用 ${=name} 的形式分割一个变量

扩展

扩展Expansion

冒号修饰符

冒号用来修饰变量,主要用于文件名。对于变量,使用 $a:r 形式,对于字面字符串,可使用 .(:a) 形式的路径扩展

a
绝对路径
A
绝对路径,解析符号链接
c
命令名->完整路径
e
扩展名
h
去掉最后一项,类似 dirname
l
小写
p
打印,但不执行。用于历史扩展
q
用引号括起,并进行适当的转义
Q
去掉一层引号
r
去掉扩展名
s/l/r[/]
替换一次出现。使用 gs 或者后加 :G 修饰来替换所有出现
&
重复上一次替换
t
类似于 basename
u
大写
x
按空格分隔后再加引号(如同 q 那样)

补全

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的历史记录文件打开为乱码,这是因为在 input.c 中的 shingetline() 函数中处理过。得到正确的文本的 C 程序如下:

//=====================================================================
// 让 zsh 的历史记录可读
//=====================================================================
#include<unistd.h>
//---------------------------------------------------------------------
void readhist(){
  unsigned char c, d;
  int change = 0;
  while(read(0, &c, 1)){
    if(c != 0x83){
      if(change){
	d = c ^ 32;
      }else
	d = c;
      write(1, &d, 1);
      change = 0;
    }else
      change = 1;
  }
}
//=====================================================================

将文本还原的程序如下:

static int ismeta(unsigned char ch){
  return (ch > 0x83 && ch < 0x9e)
    || ch == 0xa0 || ch == 0x83 || ch == 0;
}

void writehist(){
  unsigned char c, d;
  while(read(0, &c, 1)){
    if(ismeta(c)){
      d = 0x83;
      write(1, &d, 1);
      d = c ^ 32;
      write(1, &d, 1);
    }else
      write(1, &c, 1);
  }
}

模块

zpty

zmodload zsh/zpty
zpty test ls ~    # test 是名字
zpty -r test > a  # 读取输入到文件 a
zpty -d test      # 删除名字为 test 的(省略名字则删除全部)
zpty              # 列出全部(已完成和未完成的)

奇怪的是, zpty -r 的结果不能直接传给 less

应用示例

ptyless () { # 使用伪终端代替管道,对 ls 这种“顽固分子”有效 {{{2
  zmodload zsh/zpty
  zpty ptyless ${1+"$@"}
  zpty -r ptyless > /tmp/ptyless.$$
  less /tmp/ptyless.$$
  rm -f /tmp/ptyless.$$
  zpty -d ptyless
}

其它功能

sticky-note

简易的在命令行上的笔记记录,使用zsh命令行历史相同的格式存储于文件 $STICKYFILE ,默认为 ~/.zsticky

启用

autoload sticky-note

连续两次回车退出。要查看之前的笔记,只需使用 ^P 等查阅历史记录。

清理

重定向

pipe stderr ,保留 stdout [1]

3>&1 1>&2 2>&3

multios

打开 multios 选项时,重定向的顺序有关系。如以下命令

cat file >&1 > output

会将文件输出到标准输出的同时,输出到文件 output 。但顺序反过来则不行。

在标准输出上显示标准错误,同时将两者都记录到文件中

cmd 2>&1 &> log

扩展

历史记录扩展

指定历史记录
  • 上条记录: !! 或者 !
  • 本条记录: !#
  • 使用整数来按序号指定。使用负值来倒序指定( !-1 等同于 !!
  • 按开头向上查找: !str
修饰符

指定某个参数,使用数字或者 ^$ 。也可以使用范围:

  • x-y
  • * 表示所有参数(除去命令)
  • x* (等同于 x-$
  • - 除去最后一个参数(包括命令)
  • x- (与 x* 类似,但是不含最后一个参数)

对指定历史记录进行替换:

# 只替换一次
!!:s/regex/replacement
# 多处替换
!!:gs/regex/replacement
!!:s/regex/replacement/:G

其中的 / 分隔符也可以使用其它字符。

替换也可以用 ^a^b 的形式。多处替换时使用 ^a^b^:G

只打印处理后的历史记录,不执行:[2]

!!:p:gs/what/ever
!!:gs/what/ever/:p

进程替换

使用 <(command)>(command) 的形式,在系统支持时会使用 /dev/fd ,否则使用命名管道。使用 =(command) 形式将使用临时文件,以支持 lseek

花括号扩展

选项 BRACE_CCL 如果设置,将扩展形如 {a-f} 和 {3-9} 这样的字符类。[3]

字符串替换
${name/pattern/repl}
${name//pattern/repl}
pattern 中需要转义的特殊字符:
#
字符串开头
%
字符串结尾

例如,清理 Vim 撒消文件:

cd $VIM_UNDO_DIR
for f in *; do [[ ! -f ${f//\%//} ]] && rm -v $f; done

文件名生成

筛选

按类型:

  • / 目录
  • F 非空目录(Full
  • . 普通文件(不论执行权限)
  • @ 软链接
  • = 套接字
  • p 命名管道
  • * 可执行的普通文件
  • % 设备文件
  • %b 块设备文件
  • %c 字符设备文件

按权限:

  • r 所有者可读
  • w 所有者可写
  • x 所有者可执行
  • A 组可读
  • I 组可写
  • E 组可执行
  • R 其它人可读
  • W 其它人可写
  • X 其它人可执行
  • s set-user-id
  • S set-group-id
  • t sticky
  • fspec 指定权限

按当前环境:

  • U 当前有效用户拥有
  • G 组为当前用户的有效组

例子:

  • 断掉的软链接 (-@)
  • 空目录 (/^F)
排序

使用 oc 来指定排序方式,如: *.log(oL) 。在小括号中使用带方括号的数字来选择指定项,如 *(om[1]) 是最近修改过的文件。

c 可以是:
  • n: 文件名
  • L: 文件大小(length)
  • l: 链接数
  • a, m, c: 文件的访问时间、内容修改时间、i 节点修改时间。最近的文件最先。
  • d: 目录优先。与其它排序选项同时指定时最佳,如 odon
  • N: 不排序

使用 Oc 来降序排序。也可以使用 ^ (取反)结合 oc 达成。

选项

SH_WORD_SPLIT
bash那样,在扩展未用引用括起的变量时按空白分隔成多个字。默认不设置。

变量

数组

#数组长度
echo ${#commands}

提供默认值

如果 ZDOTDIR 被设置且不为空则使用之,否则使用减号后的值(这里是 HOME 的值):

_zdir=${ZDOTDIR:-$HOME}

设置变量默认值的同时替换

如果 OPTS 被设置且不为空则使用之,否则将其设置为 -v 并使用之:

: ${OPTS:=-v}

代码片断

检查命令是否存在

[[ -n $commands[ilua] ]]

所有非必须的内建命令(有相应的可执行文件的)

% for i in ${(k)builtins}; do [[ -n $commands[$i] ]] && echo $i || true; done | sort | xargs
[ echo false kill printf pwd test true which

bash 相比,zsh 多内建了 which

问题处理

Home 等键不正常

Arch下默认没有处理这些键。可复制Ubuntu下的 /etc/zsh/zshrc 文件。

问题

  • select 语句中,中文不能正常显示

评论

People sometimes say Perl has a difficult syntax to understand; I hope I'm convincing you how naive that view is when you have zsh.

——Zsh Guide

参见

外部链接

插件

补全插件

补丁

参考资料