文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Z shell 简介

Z shell 简介

时间:2008-05-02  来源:sdccf

文章分类: 系统安全 根据 Matt Chapman 的观点,Z shell 可以提高 shell 的交互效率。现在正是将这个秘密公开的时候了!在本文中,不仅介绍了 Z shell,而且还探讨了比其它 shell(尤其是 Bash)优越的地方。

提高交互式 UNIX shell 的体验

大多数 Linux 的开发人员和用户都或早或晚地接触过 UNIX shell。其中比较典型的是 Bash shell,有时也会是 C shell,或 Tcsh,Korn shell(IBM AIX 操作系统的缺省 shell)。但它们都很少能象 Z shell 一样可以提高 shell 交互效率并节省您的输入!对于那些不愿意学习“全新 shell”的人而言,值得注意的是 Zsh 是最接近所有其它 shell 的超集,所以您可以直接开始了。

运行 Z shell

Z shell ("Zsh") 命令通常在系统中的 /bin/zsh 中。如果在安装 Linux 时没有安装它,那么可以在大多数发行版的安装磁盘中找到它。当然也可以从 http://www.zsh.org 下载。顺便提一下,它并不只限于 Linux - 其源代码可以在大多数 UNIX 平台(包括 AIX)上成功地编译,并且还有许多二进制文件。

在本文中涉及的所有 shell 命令和设置选项都可以放在 ~/.zshrc 文件中,这样就可以在每次启动交互式 shell 时使用它们了。您也可以建立一个既可以在交互式 shell 中使用又可以在非交互式 shell 中使用的 ~/.zshenv 文件(例如 shell 脚本)。欲了解 Zsh 启动过程详情,请参阅 Zsh 文档。

命令行编辑

可以缺省地使用光标和退格键来移动和编辑输入行,就象在 Bash shell 中一样。也可以使用命令 "bindkey -e" 来启用 Emacs 绑定。这些键顺序的示例包括 Control-A(跳至行首)和 Control-K(删除到行尾)。当然也可以使用 "bindkey -v" 命令来绑定 Vi。

Zsh 具有多行编辑模式,这特别适合于短小的 shell 脚本,如下例所示:

zsh% for x in 1 2 3 for> do [we're in the middle of a "for" command so for> echo $x Zsh changes the prompt to remind us] for> done [after entering this, Zsh knows we're done 1 so it can run the command] 2 3 zsh% <Cursor-Up> [or Control-P with Emacs bindings] zsh% for x in 1 2 3 [the whole four line script appears, and do you can then move all through it, editing echo $x as required] done 

在这里将不再详述 shell 脚本,但值得一提的是,对于单行的 "for" 循环,事实上可以忽略 "do" 和 "done" 部分。

所键入的命令可以储存在历史记录中,而且有很多方法可以访问它,例如在上面示例中的 Cursor-Up,它就允许您滚动浏览以前输入的命令(当然用 Cursor-Down 也可以的)。另外一些有用的小窍门是 "!!",它可执行前一命令,和 "^old^new",它将前一个命令中的 "old" 替换成 "new" 之后再执行该命令。这尤其适用于当您输错时,或仅仅想稍微修改一下命令 -- 这要比回想此命令再直接编辑它快得多。

Zsh 甚至还有一个可以校正各种错误的拼写检查器。但如果它把您的命令 "纠正" 成您事实上并不想要的意思的话就会很危险,所以在它检测校正后总是会提示您。另外此功能缺省设置为关闭,您必须用 "setopt CORRECT" 启用它。以下是一个示例:

zsh% setopt CORRECT [enable option] zsh% chomd u+x file.sh [we've mistyped "chmod" here] zsh: correct 'chomd' to 'chmod' [nyae]? y [Zsh detects this and asks if we want to correct it] zsh% [if yes, then the corrected command is executed] 

TAB 键的魔力

最能干的 shell 用户是一个“懒惰”的家伙!您应该用尽可能少的输入来完成您想要的东西。通过本文您会发现 Zsh 对此很擅长。

文件名的完成并不是一个新的概念;许多 shell 可提供基本的完成格式,例如 Bash shell 可使用 TAB 键,如下例所示:

bash$ ls another_file this_is_a_file_with_a_long_name bash$ more th<TAB> [press the TAB key] bash$ more this_is_a_file_with_a_long_name [the shell magically fills in the rest!] 

按下 TAB 键,shell 会为您完成文件名。考虑一下您刚才所节省的所有输入!(或者对于用鼠标从以前列表目录中剪切和粘贴文件名 - 考虑一下将手挪向鼠标然后再放回键盘所节约的时间。)

当然,它一点也不神奇,只是减少一些烦琐的工作 - 您正在输入一个以 "th" 开头的文件名,在当前目录下仅有一个文件是以 "th" 开头的, 因而这有可能就是您要的那一个。所以,让 shell 为您做这件事吧!

以上的示例是用 Bash shell, 当然 Zsh 也可以做到这一点!而且还可以做到更多。让我们来看一下另外一个示例:

bash$ ls another_file this_is_a_file_with_a_long_name this_is_another_file bash$ more th<TAB> bash$ more this_is_a<TAB> [beeps, try pressing TAB again ] bash$ more this_is_a this_is_a_file_with_a_long_name [the shell gives you a list of this_is_another_file possible completions] bash$ more this_is_an<TAB> [type more of the required filename] bash$ more this_is_another_file [the shell can then complete it] 

正如您从这个示例中看到的,当有不止一个匹配的时候,它就会变得复杂一些。Bash shell 将尽可能多的完成文件名,然后停止。然后您就得输入文件名较多的字符,这样,在将完成它之前就是唯一的了。Zsh 缺省的情况与之很类似,但是通过设置几个选项,我们可以使之友好些。我建议将 "setopt AUTO_LIST AUTO_MENU" 放到您的 ~/.zshrc 文件中,或者在命令行中输入它。我们可以用 Zsh 尝试下面的示例:

zsh% more th<TAB> zsh% more this_is_a [the shell completes as much as it can this_is_a_file_with_a_long_name AND displays all the matching names] this_is_another_file zsh% more this_is_a<TAB> [press TAB again] zsh% more this_is_a_file_with_a_long_name [completes to the first match] zsh% more this_is_a_file_with_a_long_name<TAB> [press TAB to rotate through all matches] zsh% more this_is_another_file 

shell 在您第一次按下 TAB 时就显示所有的匹配,然后,您就可以不断地按 TAB 键来进行选择。您可以象在 Bash 的示例中那样输入较多的文件名字母,但是如果匹配的数量很少,则循环浏览它们,要比计算出需要输入哪个或哪些字符以使文件名唯一要更容易。

Zsh 还可以提供可编程的完成。这表示它完成的不仅仅是个文件名 -- 事实上是任何事,从命令到别名到环境变量都可以。同时所有这些可编程的内容大多数都作为缺省启用的,这样您就不需要做任何配置就可从中受益(尽管我们也会尝试几个简单的定制)。

让我们从命令完成开始,它也可以节省输入,在您不能准确记起命令名称的时候确实非常有用。简单地输入命令的第一部分,然后按下 TAB 键,您就可以得到尽可能完整的列表就象文件名一样。shell 知道输入的第一部分将是命令而不是文件名,所以它将对照着命令,包括内部命令和外部命令 (使用 PATH 变量定位),还有 shell 函数和别名来完成。值得一提的是 "which-command" 函数在这里很有用。在完成之后,或者输入命令后,运行缺省设置绑定到 Esc-?(按下然后松开 Escape,然后按下 "?")的函数。它简单地告诉您命令在哪里,就好象您在命令后输入 "which" 一样。这里有一个示例:

zsh% xf<TAB> [complete against commands] xf86config xfd xfindproxy xfontsel xfs xfwp zsh% xfo<TAB> [type another letter to make it unique, or TAB repeatedly to cycle] through completions] zsh% xfontsel [command has now been completed] zsh% xfontsel<Esc-?> [use Esc-? shortcut] zsh% which-command xfontsel [this shows expansion of shortcut] /usr/X11R6/bin/xfontsel zsh% xfontsel [afterwards we're back to the command itself] 

另外一个真正对您完成有用的是环境变量。任何时候您都可以使用 "$" 符号来引用变量,shell 将参照此范围内所有变量来完成。所以如果您先执行 "echo $D",然后按下 TAB, 则如果您没有其它也可与之匹配的变量,它将以 "$DISPLAY" 完成,(如果有的话,您会得到一个和前面一样完整的列表 -- 无论您匹配什么,“完成功能”都以同样的方式工作)。

正如前面所提及的,您可以完全用编程来完成,以确定完成什么及何时完成。这都是由 "compctl" 命令控制的。(Zsh 的新 beta 版本是建立在先前的 3.1.6 版本上,也包括一个新的我们在此不会涉及的机制,因为它们的使用并不广泛。)这是很复杂的领域,但是大多数都可以通过几个短的 "compctl" 行来得到。

首先,也是 "compctl" 最有用的功能之一,是基于上下文对文件名的完成进行过滤。在给定的情形下,我们可以减少相关匹配的数量,从而可增加唯一匹配的机会,因此我们就可以完整得到整个输入的内容。让我们以 Java 的使用为例。当我们想编译 Java 源文件时,我们通常会执行 "javac source.java"。源文件总是有一个 ".java" 的扩展名,所以我们可以使用此信息来限制 Zsh 在此情况下所做的匹配。以下是实现方法:

zsh% ls MyPackage TestProgram.class TestProgram.html TestProgram.java zsh% javac T<TAB> [we want to recompile source file] TestProgram.class TestProgram.html TestProgram.java zsh% javac TestProgram. [only matches as far as this] zsh% javac TestProgram.j<TAB> [so we have to type another letter] zsh% javac TestProgram.java [filename is finally complete] zsh% compctl -g '*.java' javac [restrict matches when command is javac] zsh% javac T<TAB> zsh% javac TestProgram.java [only one match now, completes immediately!] zsh% ls MyPackage [what if we have a package directory?] Main.class Main.java zsh% javac M<TAB> zsh% javac M<TAB> [oh dear, no match for directory] zsh% compctl -g '*.java' + -g '*(-/)' javac [expand command to match directories too] zsh% javac M<TAB> zsh% javac MyPackage/ [directory now completes] zsh% javac MyPackage/<TAB> zsh% javac MyPackage/Main.java [only one match inside directory] 

上面的示例显示我们得如何精简我们的 "compctl" 行来说明目录,否则在我们试图完成目录名称的时候,shell 就会发出蜂鸣声。但是我们就会有个功效强大的机制 -- 可以仅仅按下三个键:"M",然后按 TAB 键两次,就能够输入 "MyPackage/Main.java"。

这里有一些更多有关 ~/.zshrc 文件中 "compctl" 行的示例,它可以使您入门:

compctl -g '*.gz' + -g '*(-/)' gunzip gzcat compctl -g '*(-*)' + -g '*(-/)' strip compctl -g '*.ps *.eps' + -g '*(-/)' gs ghostview psnup psduplex ps2ascii compctl -g '*.dvi' + -g '*(-/)' xdvi dvips compctl -g '*.xpm *.xpm.gz' + -g '*(-/)' xpmroot sxpm pixmap xpmtoppm compctl -g '*.fig' + -g '*(-/)' xfig compctl -g '*(-/) .*(-/)' cd compctl -g '(^(*.o|*.class|*.jar|*.gz|*.gif|*.a|*.Z))' more less vi compctl -g '*.html' + -g '*(-/)' appletviewer 

这些行中的大多数都无需解释,因为它们与 "javac" 示例很相似,尽管您可能对这些语法不太熟悉,如与可执行文件相匹配的 "*(-*)" 和逻辑非操作 "(^..)"-- 在上面的行里,命令中的许多扩展名,象 "more" 和 "vi" 都可以取消,因为这些通常只是文本文件中的操作。

这里有个略微复杂些的示例,它使用定制函数来生成该完成。回到我们使用过的 "javac",在编译完源代码之后我们可能想用命令 "java TestProgram" 来运行它。如果使用缺省的文件名完成,我们只会得到 "java TestProgram." 和一个匹配的列表。然后我们除去 "." 就可以了。但是 Zsh 甚至可以比这做得还要好。您给 "compctl" 一个可返回匹配列表的函数名,匹配列表在名为 "reply" 的变量里,在必要时也可调用该函数,并将其输出作为匹配列表。所以我们在当前目录里可以定义列出所有 ".class" 文件的函数,然后除去 ".class" 的扩展名,返回这个新列表。这里有一个这样的实现及其用法:

zsh% ls MyPackage TestProgram.class TestProgram.html TestProgram.java zsh% echo $(ls *.class) [use command substitution to generate TestProgram.class list of .class files] zsh% echo ${$(ls *.class)%.class} [combine with parameter expansion to TestProgram remove .class extension] zsh% myclasslist () { reply=(${$(ls *.class)%.class}) } [define our own shell function] zsh% compctl -K myclasslist java [use this function with the java command] zsh% java <TAB> [now we can type "java", followed by a space, then press the TAB key...] zsh% java TestProgram [...there is only one .class file in the current directory, so everything is completed for us!] 

Shell 提示

在到目前为止的示例中,我们一直在使用没有包含一些有用信息的 "zsh%" 提示符。无需多说,Zsh 是一个可完全配置并可传达多种有用信息的提示符。缺省的提示符是由 PROMPT 变量控制的。这里有一些示例:

zsh% PROMPT='%/> ' [displays the current working directory] /home/matt/tmp> cd /usr/bin /usr/bin> cd /home/matt> /home/matt> PROMPT='%~> ' [use short form of current directory] ~> cd /tmp [~ is used for home directory] /tmp> cd ~> cd /usr/local/bin /usr/local/bin> LBIN=/usr/local/bin [you can give directories names to shorten the prompt] ~LBIN> pwd /usr/local/bin ~LBIN> cd ~> PROMPT='%m> ' [the first part of the hostname (use %M for the full hostname)] kheldar> PROMPT='[%!]> ' [current history event number] [232]> pwd /home/matt [233]> cd /tmp [234]> !232 [the event number is useful for pwd using history commands] /tmp [235]> 

Zsh 同样支持右边提示符,它使用支持相同语法的 RPROMPT 变量。一些人发现可变长的左边提示符(例如显示路径名的时候)经常更改令人心烦,因为它意味着您开始键入命令的位置经常变化。但是能够一眼看出当前的目录是很有用的 -- 它总是实时保存输入的 "pwd"。所以这使得对于 RPROMPT 来说,'%~' 是个理想的设置。在提示符中有当前目录的另外一个问题,是当路径名变得很长的时候,就没有足够的空间供您输入命令。但您会很高兴地看到 Zsh 很有效地处理这种情况。如果您的命令与右边提示符十分相似,则提示符会自动消失,以留下多余的空间供您输入命令。另外,如果您再删除一定数量的命令时,提示符又会再次出现。

提示符的另外一个很有用的地方是使用有强烈的视觉效果的颜色。现在大多数的终端仿真器只支持有限的颜色(可以通过使用控制代码来控制这些颜色)。通常的换码序列是 "ESC-[31m",其中 "1" 指定前景颜色,可以是任何从 0(缺省)到 9 的任何数字。如果使用回显命令,换码可以使用 "33" 进行输入,或在 Escape 键后按下 Control-V 键(引用 - 插入功能)直接将其插入 shell 中。它随后会作为 "^[" 出现,而您可以将光标移到它上面来检查它是否是单个字符 -- 它会一个一个地跳到下一个字符上。另一个需要注意的是需要告诉 Zsh 在提示符中哪些字符构成换码序列,否则就会假设它们都是可打印字符,光标的位置就会出错。这通过使用 "{%...%}" 语法来封装所有换码序列来完成。让我们用下面这个示例来说明以上内容:

zsh% echo '33[31m hello 33[30m' [see if we can change color - "1" is red for us (actual colors may 
hello
vary), and "0" is the default] zsh% echo '^[[31m hello ^[[30m' [try with escape sequences - use Control-V, then Escape to
hello
give "^[" character] zsh% PROMPT='^[[31mzsh%% ^[[30m' zsh% pwd [prompt has changed color /home/matt but cursor positioning is wrong] zsh> PROMPT='%{^[[31m%}zsh%% %{^[[30m%}' [use "%{...%}" to enclose each sequence]
zsh% pwd [positioning is then correct] /home/matt

如果您想象这样设置自己的缺省提示符,则需要进入 ~/.zshrc 文件中。有必要插入实际的换码 "^[",而不是这两个字符 "^"" 和 "["。在 Emacs/XEmacs 中可以使用 Control-Q, 然后 Escape,而在 vi/vim 中您可以使用 Control-V,然后是 Escape -,和 Zsh 一样。当然也可以使用八进制格式的控制码,"33",但是 PROMPT 设置并不能够理解这个语法本身,所以您得使用 "echo" 和命令替换,如下例所示:

以这种方法使用左边和右边提示符组合使用颜色,可以提供很多可改善交互的视觉效果。使用左边提示符可帮助您专注于您接下来要输入命令的那部分屏幕,而且它还可以将每一个命令与输出分离。有这两种提示符就可以显示更多的信息,这样您就不需要输入一些常用的命令,例如 "pwd"。就我个人而言,我把短的主机名作为我的左边提示符,因为对于一个给定的登录,它有固定的宽度,它能够清晰地区分出连接到不同主机的终端,我把当前的工作目录作为右边提示符,并以不同的颜色标识。下例说明了这一点:

 zsh% PROMPT=$(echo '%{33[31m%}%m>%{33[30m%}') kheldar>RPROMPT=$(echo '%{33[32m%}%~%{33[30m%}') kheldar>cd /usr/X11R6/bin ~ kheldar> /usr/X11R6/bin 

结束语

希望您喜欢这篇简短的 Zsh 教程。请亲自尝试这些示例来真正体会 Zsh。当然,我们仅是浅尝辄止而已。Zsh 已经开发了很多年(现在仍在继续),所以它的潜力几乎是无止境的。即使您仅使用了本文所涉及的一些功能,我希望您能够体会到 UNIX shell 环境的友好之处……

如果您已经被 Zsh 所折服,您可以通过设置 Linuxconf 面板中的 "User accounts",使之成为您缺省的 shell(或者如果您坚持己见,也可以编辑 /etc/passwd 来实现)。

原文链接:http://www-128.ibm.com/developerworks/cn/linux/shell/z/index.html

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载