远离咖啡因、繁重的工作和文本编辑器吧,是时候将 UNIX shell 改装成高速大马力汽车了,同时也是时候进行极限 shell 改造了。

Martin Streicher, 软件开发人员, Pixel, Byte, and Comma

作者照片 - martin streicherMartin Streicher 是一位 Ruby on Rails 的自由开发人员和 Linux Magazine 的前任主编。Martin 毕业于 Purdue University 并获得计算机科学学位,从 1986 年起他一直从事 UNIX 类系统的编程工作。他喜欢收集艺术品和玩具。



2011 年 7 月 18 日

无论您开什么车,您一定会喜欢高速的大马力汽车。没有什么车比配备五轮辐镀镁轮毂、宽轮胎、黑色油漆、大量镀铬部件和强劲的大马力引擎的 1969 年产 Dodge Charger 更给力了。它是一部杰作,就像 Rodin 的思考者一样,是一部经典之作。

UNIX® shell 也是一部经典之作。只要经过一番改造,它也可以像高速的大马力汽车一样强劲有力。您可以将这个改造过程称为 “极限 shell 改造”。我觉得这很酷!

它由活动部件组成

在闪动的提示符背后,您的 shell(比如 Bash、Z shell 或其他 shell)有许多活动部件,您可以增强、改进和调整它们。以下是对这些部件的总结(其中许多已在前面的专栏文章中详细讨论过):

  • 别名(alias)是缩写的命令名和命令行短语。例如,命令 alias ll 'ls -hlt' 将一个常用的命令行缩减为两个字母,从而使要输入和要记住的东西变得更少。别名通常只是单纯的缩写。但是,与 shell 脚本或 shell 函数一样,别名也可以接受参数。命令 alias print 'lpr -h -Pps5 \!*' 可以将 \!* 替换为命令行参数。因此,print manual.ps schematic.ps form.ps 会展开为 lpr -h -Pps5 manual.ps schematic.ps form.ps
  • 环境变量跨命令(甚至是生成新进程的命令)保存设置。根据惯例,一些环境变量有特殊含义:命令和应用程序认为 PRINTER 表示首选输出设备,而 EDITOR 和 PAGER 分别表示用来修改和显示文本的文本编辑器和查看器。其他环境变量与不同的实用程序有关。PS1 就是这样一个例子,它告诉 shell 将显示哪些内容作为最初提示。另一个重要的环境变量是 shell 的 PATH,它列出了搜索可执行文件的目录。(要想了解特定应用程序或命令中可以识别哪些环境变量,请参阅其手册页中的 "Environment variables" 部分。)
  • 函数也可以从命令行调用,它们填补了别名与完全成熟的脚本之间的空缺。例如,如果需要操纵参数或应用逻辑,别名很可能不够用;但是,使用完整的脚本可能太过浪费。因此可以通过组合使用函数来实现累积的效果。创建函数需要一定的编程经验,但是并不不是很麻烦。
  • shell 选项控制了 shell 的行为。shell 选项通常因 shell 而异,功能丰富的 shell(比如 Bash 或 Z shell)可能有数百个可调整参数。(Z shell 有专门对配置选项进行解释的手册页 zshoptions。)例如,如果已经启用了 Z shell 选项 pushd_ignore_dupspushd 就不会将已存在的目录压入目录堆栈。您可以花几小时研究选项,寻找想要的组合,然后执行 set (Bash) 或 setopt 并将输出重定向到某个文件中。可以将捕捉到的部分或所有设置复制到启动文件中,这样就可以在每次打开 shell 时重建自己的工作空间。

除了这些部件之外,还可以改变 shell 的外观。美元符号 ($) 提示符可以显示为不同的颜色,以反映当前工作目录,甚至是天气情况。只要可以用命令捕捉到信息,就可以将它们显示在提示符中。


修改提示符

与 shell 中的其他操作一样,环境变量可以控制每次 shell 显示提示符时应显示的内容。与命令行本身一样,可以在呈现提示符内容时对变量 PS1(或 "prompt string level 1")进行解释。PS1 可以包含其他 shell 和环境变量、置换命令运算(通过反引号)和特殊的字面值。下面是一个简单的示例:

$ export PS1="\u@\h \w >"
strike@nostromo ~ > whoami; hostname; pwd
strike
nostromo
/home/strike
strike@nostromo ~ >

最初,提示符是简单的 $ 符号。如果将环境变量 PS1 设置(使用 export 而不是 set)为 \u@\h \w >,则会显示您的用户名 (\u)、字面值 @、主机名 (\h)、当前工作目录 (\w) 和字面字符串 >

其他特殊的字面值包括 \t(采用 24 小时制的时间格式)、\d(采用周、月、日格式的日期)和 \!(shell 历史记录编号)。如果 shell 窗口打开的时间较长,可以用 !nnn 快速重复前面的命令,其中的 nnn 是命令提示符中显示的编号。

strike@nostromo ~ > export PS1="\! $ "
776 $ find . -name ...
...
999 $ !776
find . -name ...
1000 $

当您的行为类似于超级用户(即 root 用户)时,shell 提示符会变成 #,您可能对此感到惊奇。默认情况下,标准的提示符实际上是特殊操作符 \$。如果您的有效用户 ID 是 0,则会使用 # 作为提示符;否则会使用 $

还有用来改变颜色的缩写。下面是一个示例:

strike@nostromo ~ > export PS1='$ '
$ blue='\e[0;34m'
$ none='\e[m'
$ export PS1="$blue\u@\h$none\w>"
<span style="color: blue;">strike@nostromo </span>~ ><br />

十分笨拙的缩写 \e[0;34m 表示可以启用蓝色。\e[m 可以将显示颜色恢复为 shell 窗口的默认颜色。这两个编码均放在单引号内,以防止 shell 篡改有特殊含义的字符。这个示例还说明可以在提示符中使用变量。在解释命令行时,会展开变量 bluenone,提示符被设置为展开变量而生成的字符串。如果希望在每次显示提示符时都动态展开变量,则必须在设置提示符时对它的解释进行转义。请参见以下示例:

<span style="color: blue;">strike@nostromo 
    </span>~ >export PS1="\$somevar $ "
$ somevar="hello"
hello $

\$somevar 可以对美元符号进行转义,避免在设置提示符的命令中对该变量进行解释,而是在显示提示符时对其进行解释。如果其他命令改变了 somevar 的值,提示符将显示为新的值。

正如前面提到的,还可以在提示符下调用任何命令或函数。只需使用反引号 (``)。例如,在使用 Ruby Version Manager 在不同的 Ruby 语言版本和解释器之间切换时,您可能想知道当前使用了哪种 Ruby 二进制代码。有两种实现此操作的方法:

hello $ export PS1="(`which ruby`) \w $ "
(/Users/strike/.rvm/rubies/ruby-1.9.2-p0/bin/ruby) ~ $

第一种方法使用 which 在当前的 PATH 中寻找第一个 Ruby 可执行文件。后一种方法实现相同的目标,它展示了置换命令运算可以有多么复杂。如果将提示符设置为以下字符串:

"(`rvm info | grep 'ruby:' | grep bin | cut -f2 -d: | tr \\" ' '`)"

会得到相同的结果,尽管没必要采用这么复杂的方式。

顺便说一句,您可能想知道 PS1 为什么是 "prompt string level 1"。是否存在其他提示符级别?是的,存在针对 PS2、PS3 和 PS4 的环境变量,当打开新的块时,会出现这些提示符。下面是一个示例:

$ for i in [A-Z]*
>

输入 for i in [A-Z]* 并按回车之后,shell 会显示 PS2 提示符(默认提示符是 >),这表明您目前位于 for 循环主体中。换句话说,您现在 “嵌套” 在循环中,处于更深的一级。如果用 done 结束循环,第一级提示符会重新出现。

$ for i in [A-Z]*
> do
> echo $i
> done
Gemfile
Gemfile.lock
README
Rakefile
$

实际上,只要在前面的提示符中没有完成命令行,就会出现新提示符。这解释了不匹配的单引号或双引号会导致出现新提示符的原因。shell 会提示您继续输入前面已经开始的命令行。

在提示符中嵌入信息是跟踪状态(比如当前主机、工作目录等等)的好方法。创建您喜欢的提示符之后,将 shell 启动文件分发给您的所有帐号。如果您与大多数现代用户一样,那么您的屏幕上会有许多远程 shell 窗口,提示符可以帮助您区分它们。可以创建根据主机改变提示符颜色的函数。


modder 和 rodder

一些用户将定制 shell 当作一种消遣,您可以在网上找到很多这方面的灵感和源代码。有两个软件包非常值得一提:Oh My Z Shell! 和 Bash It!。前者适用于 Z shell,后者适用于 Bash。它们都是 shell 修改的集合,包括一些补充、主题(颜色和提示符)、函数和现成的 "dot" 文件。它们都是开放源码的,可以从 github 获得它们。让我们来试一下 Oh My Z Shell!(或简称为 OMZ!)。要想使用它,您必须安装 Z shell 4.3.9 或更高版本。

可以使用 wget 安装这个包,并自动将您的 shell 改为 Z shell,参见清单 1

清单 1. 安装 shell 并将它改为 Z shell
$ wget http://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh
...
Cloning Oh My Zsh...
Cloning into /Users/strike/.oh-my-zsh...
remote: Counting objects: 1312, done.
remote: Compressing objects: 100% (750/750), done.
remote: Total 1312 (delta 796), reused 944 (delta 520)
Receiving objects: 100% (1312/1312), 153.63 KiB, done.
Resolving deltas: 100% (796/796), done.
Looking for an existing zsh config...
Using the Oh My Zsh template file and adding it to ~/.zshrc
Copying your current PATH and adding it to the end of ~/.zshrc for you.
Time to change your default shell to zsh!
Changing shell for strike.
Password for strike:
chsh: /usr/bin/env zsh: non-standard shell
         __                                     __
  ____  / /_     ____ ___  __  __   ____  _____/ /_
 / __ \/ __ \   / __ `__ \/ / / /  /_  / / ___/ __ \
/ /_/ / / / /  / / / / / / /_/ /    / /_(__  ) / / /
\____/_/ /_/  /_/ /_/ /_/\__, /    /___/____/_/ /_/
                        /____/

还可以手工安装这个工具包。如果您在运行另一种 shell,只是想试试 Z shell,那么手工安装可能会更好。请使用 Git 复制这个工具包,然后运行 zsh

$ git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
$ cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc
$ zsh

您应该会看到与图 1 相似的屏幕和提示符。OMZ! 的默认主题称为 "robbyrussell",由 OMZ! 的管理者命名。您可以将它更改为 ~/.oh-my-zsh/themes 中列出的任何主题。更改主题的方法是,打开 ~/.zshrc 文件,将 ZSH_THEME 变量设置为主题文件的基本名称。例如,要想使用 cloud.zsh-theme,则应该设置 ZSH_THEME=cloud

图 1. 更改 shell 的主题
图中显示了 shell 的主题

您可能会注意到,许多主题的输出状态信息显示在提示符中和提示符的最右边。例如,Clean 主题在最右边输出当前时间。与硬件屏幕不同,屏幕模拟器的底部通常没有状态条,但是可以使用提示符右边的区域提供动态反馈。还记得可以在提示符中使用 \e[ 编码设置颜色吗?有一组这样的 “转义” 可以将光标移动到窗口中的不同位置。另外,目前的 UNIX 系统并没有使用神秘的符号和数字,而是使用 tput 按名称或用途查找和产生转义。Z shell 使用这种方法提供 RPS1 和 RPS2,它们分别表示初始行和后续行右边的提示符。

除了通过主题控制颜色和提示符之外,OMZ! 还提供了一些插件,这些插件将同类的函数和特性集中在一起。例如,如果您使用 Git 进行源代码控制,可以启用 Git OMZ! 插件,它会在提示符中增加 Git 状态。请打开 ~/.zshrc 文件,然后编辑插件行,在其中包含 git>。现在,当切换到一个 Git 存储库时,提示符会反映该状态。例如,图 2 显示了 Clean 主题和一个存储库,其中有包含一些尚未提交的修改。

图 2. Clean 主题和存储库
图中显示了 Clean 主题和存储库

提示显示了当前的分支 ("rubricmods") 和一个红色的 X,这表示当前存储库是 “脏的”,即已经修改了本地文件,但还没有提交。提交修改之后,X 会消失。Git 插件还为常用的 Git 命令组合创建别名,为 Git 选项提供上下文敏感的命令补全特性。来试一下:如果输入 git branch,然后按 Tab 键,Git 插件就会列出可用的分支,以及用于 Mac OS X、Ruby on Rails 开发、MySQL 等的其他插件。一些插件很简单,可能只提供一个函数或几个别名;其他插件的功能可能更丰富。

Dirpersist 是说明 OMZ! 插件的用途的好例子。Dirpersist 跨 shell 调用存储并恢复目录堆栈,因此可以保留重要的状态,让您能够回到原来离开的位置继续工作。要使用它,请在 ~/.zshrc 文件中添加这个插件。这个插件的源代码很简单,参见清单 2

清单 2. OMZ! 插件源代码
#!/bin/zsh
#
# Make the dirstack more persistent
#
# Add dirpersist to $plugins in ~/.zshrc to load
#

# $zdirstore is the file used to persist the stack
zdirstore=~/.zdirstore

dirpersistinstall () {
  if grep 'dirpersiststore' ~/.zlogout > /dev/null; then
  else
    if read -q \?"Would you like to set up your .zlogout file for 
        use with dirpersist? (y/n) "; then
      echo "# Store dirs stack\n\
        # See ~/.oh-my-zsh/plugins/dirspersist.plugin.zsh\ndirpersiststore" >> ~/.zlogout
    else
      echo "If you don't want this message to appear, remove dirspersist from \$plugins"
    fi
  fi
}

dirpersiststore () {
  dirs -p | perl -e 'foreach (reverse <STDIN>) {\
    chomp;s/([& ])/\\$1/g ;print "if [ -d $_ ]; then pushd -q $_; fi\n"}' > $zdirstore
}

dirpersistrestore () {
  if [ -f $zdirstore ]; then
    source $zdirstore
  fi
}

DIRSTACKSIZE=10
setopt autopushd pushdminus pushdsilent pushdtohome pushdignoredups

dirpersistinstall
dirpersistrestore

# Make popd changes permanent without having to wait for logout
alias popd="popd;dirpersiststore"

这个插件包含三个函数、几个 shell 选项和 popd 的别名(为每个弹出操作保留目录堆栈)。这个插件还对其环境进行初始化,创建存储目录堆栈的位置,通过修改 Z shell 退出文件 ~/.zlogout,在用户退出时保存目录堆栈。您应该可以看出,创建新的插件很容易,还可以围绕任何 shell 命令集来构建插件。

如果您喜欢 OMZ!,但是您使用的是 Bash shell,那么试一下 Bash It!。它的灵感来源于 OMZ!,具有相似的功能。Bash It! 还为 Subversion 和 nginx 提供了插件。


发动引擎吧!

研究 OMZ! 或 Bash It! 的内部机制,了解定制 shell 的一些技巧。通过在网上搜索 "dot files",还能找到一些充满智慧的、新奇的东西:许多 UNIX 爱好者在网上公开了他们的 shell 配置供他人借鉴。还有一点值得注意:应该对 dot 文件采用某种版本控制机制。有了存储库,您便可以更放心地试验各种修改和其他 shell。如果犯了错误,可以恢复以前的版本,还可以为不同的 shell 或场景保留相应的变体。shell 非常灵活,因为一种配置不一定适合所有场景。当您有了一套很酷的 dot 文件之后,请与别人分享。公开 dot 文件虽然不能使您成为《速度与激情》的下一部影片中的明星,但很可能会提高您在爱好者中的声誉。

参考资料

学习

  • Z shell:可以在 Z shell 的项目页面上找到其源代码以及丰富的文档和教程。
  • Bash 文档:进一步了解 Bash shell、终端光标和呈现选项。
  • Oh My Z Shell!:请参考这个开放源码项目,以便向 Z shell 添加扩展和有帮助的快捷方式。
  • Bash It!:这个社区项目提供了用来扩展和简化 Bash shell 的框架。
  • 许多开发人员和开放源码项目使用 Gitgithub 进行代码版本控制。
  • 对话 UNIX:阅读本系列的其他部分。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。
  • 技术书店:阅读关于这些和其他技术主题的书籍。

获得产品和技术

  • Bash shell:从 GNU Software Foundation 下载 Bash shell 的源代码。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=733386
ArticleTitle=对话 UNIX: 极限 shell 改造
publish-date=07182011