Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379310
  • 博文数量: 190
  • 博客积分: 50
  • 博客等级: 民兵
  • 技术积分: 125
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-12 14:05
文章分类

全部博文(190)

文章存档

2013年(190)

我的朋友

分类: LINUX

2013-03-06 09:56:42

研究 Z Shell (zsh) 的重要组成部分和如何使用其功能来简化您的 UNIX 系统管理任务。zsh 是一个流行的 Shell,是对原始 Bourne 和 Korn Shell 的替代。它提供了印象深刻的系列附加功能,包括用于自动完成不同命令、文件和路径以及用于将键绑定到功能和操作的改进。

关于本系列

典型的 UNIX 管理员拥有一套经常用于辅助管理过程的关键实用工具、诀窍和系统。存在各种用于简化不同过程的关键实用工具、命令行链和脚本。其中一些工具来自于操作系统,而大部分的诀窍则来源于长期的经验积累和减轻系统管理员工作压力的要求。本系列文章主要专注于最大限度地利用各种 UNIX 环境中可用的工具,包括简化异构环境中的管理任务的方法。

zsh 背景介绍

UNIX 和 Linux 环境下的 Shell 通常可归入两个类别之一,这两个类别基于最初的 UNIX 版本所附带的原始 Shell。这两个类型分别是 Bourne Shell 和 C Shell;后者的独特之处在于其格式和结构类似于 C 编程语言的格式和结构。

Bourne Shell 比 C Shell 更容易使用和理解,但是对于您可能希望在 Shell 编程环境中实现的复杂脚本编程,它可能就不太实用了。Korn Shell 提供了 Bourne Shell 的易用性和附加的作业控制扩展(允许您容易地管理多个后台作业)、命令行编辑和历史记录,以及用于简化编程的附加 C Shell 元素。

Z Shell (zsh) 是在考虑交互式使用而不是编程的情况下设计的,因此它整合了大量显著简化命令使用和运行的功能。这些功能的示例包括更广泛的文件名匹配 (globbing)、用于重定向输入和输出的多个 I/O 流,以及一个可完全自定义的命令行完成系统。

文件名生成

文件名 globbing 是将文件名或文件规范转换为供命令行(例如,在复制或移动文件时)使用的文件列表的后台过程。基本文件名 globbing 包括使用 ? 来代表单个字符,或使用 * 来代表一个或多个字符。

例如,若要列出所有的 C 源文件,您可以使用 清单 1。

清单 1. 列出所有的 C 源文件
  1. $ ls *.c
  2. barney.c        betty.c         fred.c          wilma.c
复制代码
并且您可以使用字母集合(就像您可能在正则表达式中使用的那样),例如,用于列出具有“c”或“o”扩展名的文件,如清单 2 所示。

清单 2. 列出具有“c”或“o”扩展名的文件
  1. $ ls *.[co]
  2. barney.c        betty.c         fred.o
  3. barney.o        fred.c          wilma.c
复制代码
使用 zsh,相同的通配符仍然有效,但是您还可以使用扩展的 globbing 来指定附加选项。若要启用扩展的 globbing,可以设置 EXTENDEDGLOB 环境变量,或者使用: $ setopt extendedglob。

您现在可以使用诸如 ^ 字符等构造,以显示与给定的文件规范不匹配的所有文件。例如,若要列出与 *.c 规范不匹配的所有文件,可以使用清单 3。

清单 3. 列出与 *.c 不匹配的文件
  1. zsh$ ls ^*.c            
  2. barney.o        betty.h         fred.h          fred.o
复制代码
表达式 匹配一系列整数。例如,如果您已使用日期对访问日志归档,则可以选择在某个特定日期范围内的所有文件。为实现这点,可以按顺序使用年、月、日来对文件命名,并使用零来补齐空缺。例如,若要列出 2006 年 11 月 3 日和 10 日之间的日志,您可以使用清单 4。

清单 4. 列出 2006 年 11 月 3 日和 10 日之间的日志
  1. zsh$ ls access<20061103-20061110>.log
  2. access20061103.log      access20061106.log      access20061109.log
  3. access20061104.log      access20061107.log      access20061110.log
  4. access20061105.log      access20061108.log
复制代码
您还可以使用类似于正则表达式的组来匹配文件。例如,使用清单 5 来列出所有名为 fred 和 barney 的文件。

清单 5. 列出所有 fred 和 barney 文件
  1. zsh$ ls (fred|barney)*
  2. barney.c        fred.c          fred.o
  3. barney.o        fred.h
复制代码
通过使用 **/,还可以搜索子目录;该过程是递归的,例如 find 命令,以便您能产生 find 命令的等效结果,例如 $ find . -name "*.c"。

在 zsh(带扩展的 globbing)中,这可以扩展为: zsh$ ls **/*.c。

您也可以组合进一步的示例,例如使用清单 6 中的命令来查找子目录中的所有 C 源文件和编译后的目标文件。

清单 6. 查找所有 C 源文件和编译后的目标文件
  1. zsh$ ls **/*.(c|o)
  2. glob/barney.c   glob/betty.c    glob/fred.o
  3. glob/barney.o   glob/fred.c     glob/wilma.c
复制代码
最后,zsh 支持许多后缀修饰符。若要获取可执行文件列表,可以在 globbing 表达式结尾使用后缀修饰符 (*)(请参见清单 7)。

清单 7. 获取可执行文件列表
  1. zsh$ ls *(*)
  2. barney  fred
复制代码
您还可以在 globbing 表达式结尾使用 (x) 来获取可执行文件列表(请参见清单 8)。

清单 8. 使用 (x) 来获取可执行文件列表
  1. zsh$ ls *(x) 
  2. barney  fred
复制代码
上述列表文件可由文件的所有者执行。大写的 X 将选择可由其他人执行的文件。同样的原理也适用于文件的 R/r(可读取)和 W/w(可写入)模式。 

命令或进程替换

在大多数 Shell 环境中,您都可以使用基本的进程替换来将一个命令的输出嵌入到另一个命令的输入或参数中。这可以使用反引号运算符来实现(请参见清单 9)。

清单 9. 使用反引号运算符来执行进程替换

                
$ emacs `find . -name "*.html"`


您可以在 zsh 中使用许多选项。最主要的方法是使用 $() 构造。此构造提供对反引号的直接替代,并且它还具有更容易在某些命令组合中嵌入和嵌套的优点。例如,您可以将清单 9 重写为清单 10。

清单 10. 对使用反引号来执行进程替换的替代
  1. zsh$ emacs $(ls **/*.html)
复制代码
这里的进程替换运行目录列表组件并返回一个文件名列表,后者又提供给 emacs 命令的参数列表。

另一个有用的构造是 =(list) 结构。当使用此功能时,括号中的元素会生成一个临时文件,并返回该文件的名称。例如,您可以使用清单 11 来生成一个文本文件。

清单 11. 生成一个文本文件
  1. zsh$ cat =(print -l tom dick harry)                            
  2. tom
  3. dick
  4. harry
复制代码
更有用的是,您可以将它与其他元素组合,以支持更复杂的输出和过滤。例如,您可以使用以下命令(请参见清单 12)来获取与 imapd 和 httpd(IMAP 邮件服务和 Apache http 服务)匹配的进程列表。

清单 12. 获取与 imapd 和 httpd 匹配的进程列表
  1. zsh$ ps -ax |fgrep -f =(print -l httpd imapd)
  2. 406 ?? Ss  0:02.05 /usr/sbin/httpd
  3. 426 ?? S   0:01.32 /usr/sbin/httpd
  4. 429 ?? S   0:06.42 imapd: sulaco.mcslp.pri [192.168.0.101] appleblog user.appleblog
  5. 434 ?? S   0:57.81 imapd: sulaco.mcslp.pri [192.168.0.101] mcarc user.mcarc
  6. 435 ?? S   0:00.14 imapd: sulaco.mcslp.pri [192.168.0.101] mlists user.mlists
  7. 436 ?? S   0:00.12 imapd: sulaco.mcslp.pri [192.168.0.101] play user.play
  8. 437 ?? S   0:01.16 imapd: sulaco.mcslp.pri [192.168.0.101] mc user.mc
  9. 507 ?? S   0:01.25 /usr/sbin/httpd
复制代码
在清单 12 中,print 命令在单独的行上将每个参数输出到一个临时文件。然后 fgrep 使用该文件作为针对进程列表的匹配列表。此临时文件在该命令完成后被删除。

由于该功能从输出创建临时文件,您可以使用它来执行以前要求您自己创建并删除临时文件的功能和序列。例如,若要比较两个不同目录的文件列表,您可以将每个目录中的文件列表输出到一个临时文件,然后使用 diff 命令来比较输出。使用 zsh,在单个命令行上即可实现相同的结果(请参见清单 13)。

清单 13. 比较两个不同目录的文件列表
  1. zsh$ diff =(ls new) =(ls old)
  2. 3d2
  3. < barney.o
  4. 9d7
  5. < fred.o
复制代码
在此示例中,两个被替换的 ls 命令生成一个临时文本文件,然后 diff 命令对该文件进行内联比较。 

多个 I/O 流

与进程替换系统相关的另一个功能是更广泛的重定向系统。在大多数 Shell 中,您都习惯于针对文件执行重定向(请参见清单 14)。

清单 14. 针对文件执行重定向
  1. $ crontab
  2. $ ls > filelist
复制代码
现在使用 zsh,您可以同时重定向到多个输出(请参见清单 15)。

清单 15. 同时重定向到多个输出
  1. zsh$ ls >listone >listtwo
复制代码
您还可以从多个流输入(请参见清单 16)。

清单 16. 从多个流输入
  1. zsh$ sort
复制代码
管道是隐式的重定向,因此其工作方式大致相同。例如,在任何 Shell 中,您都可以使用 tee 命令来重定向文件和标准输出(请参见清单 17)。

清单 17. 使用 tee 命令来重定向文件和标准输出
  1. $ ls | tee listone
  2. barney
  3. barney.c
  4. barney.o
  5. betty.c
  6. betty.h
  7. fred
  8. fred.c
  9. fred.h
  10. fred.o
  11. wilma.c
复制代码
在 zsh 中,您可以使用清单 18。

清单 18. 在 zsh 中重定向到文件和标准输出
  1. zsh$ ls >fileone |cat
  2. barney
  3. barney.c
  4. barney.o
  5. betty.c
  6. betty.h
  7. fileone
  8. fred
  9. fred.c
  10. fred.h
  11. fred.o
  12. wilma.c
复制代码
作为进程替换的扩展,通过使用 <(list) 和 >(list) 构造,您还可以针对另一个命令进行重定向(请参见清单 19)。

清单 19. 使用 < 和 > 来针对另一个命令进行重定向
  1. zsh$ sort <(ls) <(ls /usr)      
  2. X11R6
  3. barney
  4. barney.c
  5. barney.o
  6. betty.c
  7. betty.h
  8. bin
  9. fileone
  10. fred
  11. fred.c
  12. fred.h
  13. fred.o
  14. include
  15. lib
  16. libexec
  17. local
  18. sbin
  19. share
  20. standalone
  21. wilma.c
复制代码
在清单 19 中,您将两个 ls 命令的输出组合为 sort 命令的标准输入,从而输出两个不同目录中的文件的混合和排序列表。

此功能的一个典型使用场合是使用剪切和粘贴来将一个文件中的字段提取并重新组合到另一个文件中。对于普通的 Shell,您可能会使用许多临时文件(请参见清单 20)。

清单 20. 使用临时文件来将一个文件中的字段提取并重新组合到另一个文件中
  1. $ cut -f1 fileone >t1
  2. $ cut -f5 filetone >t5
  3. $ paste t1 t5
复制代码
在 zsh中,您无需临时文件即可完成此任务,如清单 21 所示。

清单 21. 在 zsh 中剪切和粘贴以提取和重新组合文件中的字段
  1. zsh$ paste -d: <(cut -d: -f1 /etc/passwd) <(cut -d: -f5 /etc/passwd)
复制代码
此外,由于可以容易地嵌套重定向的替换,您可以创建复杂的结构,例如以与源文件不同的顺序组合 passwd 文件中的两个字段、删除注释,然后对它们排序(请参见清单 22)。

清单 22. 在 zsh 中创建复杂结构
  1. zsh$ sort <(egrep -v '^#' <(paste -d: <(cut -d: -f5 /etc/passwd) <(cut -d: -f1 
  2. /etc/passwd) ) )
  3. :# mode.  At other times this information is handled by one or more of
  4. Amavisd User:amavisd
  5. Apple Events User:eppc
  6. Application Owner:appowner
  7. Application Server:appserver
  8. ...
  9. Xgrid Agent:xgridagent
  10. Xgrid Controller:xgridcontroller
  11. sshd Privilege separation:sshd
复制代码
通过按清单 23 所示来查看每个元素,您可以简化清单 22。

清单 23. 简化的过程

                
-        <(cut -d: -f1 /etc/passwd) - Get the first field
-        <(cut -d: -f5 /etc/passwd) - Get the fifth field
-        <(paste -d: <(f5) <(f1) ) - Recombine them in a different order
-        <(egrep -v '^#' <(paste...) - Remove the comments
-        sort <(egrep ...) - Sort the standard input

文件和命令完成

在某些 Shell (包括 zsh)中,您可以通过按 TAB 键来完成文件或命令。让我们使用当前目录作为示例(请参见清单 24)。

清单 24. 当前目录清单
  1. zsh$ ls
  2. barney          betty.c         fred            fred.o
  3. barney.c        betty.h         fred.c          wilma.c
  4. barney.o        fileone         fred.h
复制代码
使用完成功能,您可以输入文件名的开头:zsh$ cd bar。然后按 TAB 键并获得完整或部分完成: zsh$ cd barney.

第二次按 TAB,您将看到可用文件列表(请参见清单 25)。

清单 25. 获得可用文件列表
  1. zsh$ cd barney
  2. barney*   barney.c  barney.o
复制代码
同样的完成过程也适用于目录和路径。zsh 在完成功能方面做了进一步的优化。 

自定义完成功能

标准形式的完成功能的局限性在于,它只能在您从命令行上执行输入时完成文件和路径(包括命令)。然而,在其他许多领域,您可能也希望能够完成命令而不必完成所有的输入。例如,源代码控制系统 Subversion 提供了许多二级命令,您在输入其他参数之后另外输入这些命令。例如,若要将更改提交回 Subversion 存储库中,您可以使用 commit 命令:$ svn commit。或者,若要更新,您可以使用 update 命令: $ svn update.

但您必须人工执行这些输入。通过使用 zsh 中的自定义完成控制,您可以作为完成过程的一部分来将这些子命令添加到 svn。完成控制是非常复杂(并且有时非常混淆)的系统,但是其基本原理是相当容易理解的。

完成功能是通过许多命令来控制的,但是最主要的命令为 compctl。此命令为基本的完成功能提供了一个简单接口。可以全局地应用完成功能(换句话说,就如文件和路径),或者可将其应用于特定的命令。

存在一系列可用的选项和格式,但是为了延续 Subversion 思想,您可以使用 -k 选项来提供一组单词,充当 svn 命令的潜在完成目标(请参见清单 26)。

清单 26. 使用 -k 选项
  1. zsh$ compctl -k '(commit checkout update status)' svn
复制代码
现在,当您在命令开头输入 svn 时,您需要按 TAB 来完成该命令(请参见清单 27)。

清单 27. 按 TAB 来完成命令
  1. zsh$ svn com
  2. zsh$ svn commit
复制代码
完成系统包括标准的功能和完成特性——例如,系统上的有效用户、主目录、主机、网络等对象的查找。您还可以自己添加和扩展这些功能来产生自定义的完成结果。

例如,您可以创建一个名为 activeusers 的自定义功能,用于返回用户命令产生的输出: zsh$ function activeusers { reply =(`users`) }。

您现在可以使用此功能作为另一个命令的完成目标,例如 chat 命令: zsh$ compctl -K activeusers chat。

现在,当您在命令行上输入 chat 时,可以获得仅返回当前登录用户列表的完成列表。
阅读(1434) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~