你必须非常努力,才能看起来毫不费力!
分类: 系统运维
2008-10-17 17:59:31
Shell 快捷方法可以减少键入等重复性劳动,从而节省精力和时间
级别: 中级
Martin Streicher (), 主编, Linux Magazine
2006 年 9 月 11 日
了解如何利用 UNIX® Shell 提供的许多快捷方法。通过一些练习,您将可以更灵活地、而不是更辛苦地进行工作。
每种技术性劳动都有其自身的秘密,这些小窍门、技巧和工具甚至可以处理最复杂的任务。例如,我的邻居是一个熟练的木匠。他仅凭眼睛就可以非常精确地测量和改变角度、无缝地进行斜接,并且他所完成的作品为他在当地报纸上赢得了赞誉。
但更神奇的是(至少对于我这样一个肯定会出错的外行来说),他可以相当轻松地完成工作。他干这一行大约有 20 多年了,并且掌握了所有的快捷方法。通过这些快捷方法,可以在这里节省一点点时间,在那里节省一点点劳动,然而对于像进行切割、锤钉子和组装框架这样的重复性任务,这样的节省最终加起来真的不少。
程序员、系统管理员和其他的 UNIX® 计算机专业人员都有他们自己专门的工具:
正如第 1 部分所介绍的,UNIX 命令行的强大功能是无与伦比的。只需按一些键并使用一些句法粘结剂,包括管道 (|)、tee
和重定向,您就可以在 Shell 提示符中即兴组装自己的数据转换器。
例如,下面的命令将在您的 home 目录中查找所有包含单词 Monthly Report 的文本文件:
$ find /home/joe -type f -name '*.txt' -print | xargs grep -l "Monthly Report"
|
该命令将搜索整个 home 目录 (find /home/joe
) 以查找所有的常规文件 (-type f
) 中具有后缀 .txt
的文件,然后运行 grep
命令来搜索字符串 Monthly Report
。如果找到匹配项,-l
选项将打印出相应的文件名。因此,该命令的输出是匹配文件的列表。
尽管上面的命令很有用,但是要记住这个命令并重新输入,这样做很费劲,尤其是在您需要经常使用这个命令的情况下。而且,当命令行作为使用电子邮件、文件、工具(如编辑器、编译器、监视器)和远程系统的主要接口时,您在命令行中所节省的时间和精力可以更好地用于手头上的其他任务。毕竟,这些短的时间加起来真的不少。
为了处理这些重复性的任务,UNIX Shell 提供了各种有用的快捷方法,具体包括:
~
(波浪符号)引用您的 home 目录。您还可以使用 $HOME
环境变量引用您的 home 目录,如清单 1 所示。
$ whoami strike $ echo ~ /Users/strike $ echo $HOME /Users/strike $ !! echo $HOME /Users/strike |
最后一个命令 !!
(两个感叹号),可能看起来有些奇怪,但它是一种命令历史符号,可以一字不差地重复前面的命令。(许多 Shell 还允许您使用向上箭头键或按 Control+P 来浏览以前的命令列表。)
让我们更仔细地研究 Shell 中的各种快捷方法。本文主要介绍 Z Shell(zsh
,请参见参考资料部分),它通常安装在 /bin/zsh 目录中。(如果您的系统中没有 Z Shell,可以请求系统管理员安装它。)Z Shell 具有一些特别的特性,另外,这里所介绍的示例适用于所有主流的 UNIX Shell。
|
针对许多频繁使用的命令行参数,Shell 提供了相应的符号 或记号作为简写。您只需输入这些符号来代替相应的参数。
如上所述,~
表示您的 home 目录。与之类似的简写形式 ~username
表示 username 的 home 目录。例如,~joe
表示 joe 的 home 目录,所以,要将文件从 joe 的 doc 目录复制到您的 info 目录,您可以输入下面的命令:
$ cp ~joe/doc/report.txt ~/info
|
假设 joe 的 home 目录位于 /guests,而您的 home 目录为 /staff/bobr,~joe
将由 /guests/joe 替换,而 ~
则变成 /staff/bobr,最后产生命令 cp /guests/joe/doc/report.txt /staff/bobr/info
。(请参见侧栏“检验您的工作”以了解如何预览命令行。)
|
另一个有用的符号是 ..
(两个点号),这是当前目录的父目录的简写。使用 ..
和 .
(当前工作目录的简写符号),您可以引用文件系统中相对于当前工作目录的文件和目录。
例如,如果您的当前工作目录为 ~/jane/projects/lambda,那么简写 ../..
表示向上两级目录的目录,即 ~/jane。要表示包含 ~/jane 的目录,您可以使用 ../../../
(“向上三级目录”)或路径 ~jane/../
。后面的这个路径表示从 ~jane 开始,然后转到上一级目录。
要将文件复制到您的当前目录,不需要指定目标目录,可以直接使用 .(“点”)来表示:
$ cp -pr /path/to/lots/of/stuff .
|
前面的命令将 /path/to/lots/of/stuff 目录递归地复制到您的当前目录,并保持其原始的时间和日期戳。引用 ..
和 .
的路径名称为相对路径名。以 /(正斜杠)或 ~(波浪符号)开头的路径名称为绝对路径名,因为您是从文件系统的顶端、或从一个目录层次结构的顶端开始来引用相应的文件。
|
使用符号,可以节省输入的时间,并且可以快速和精确地引用特定的目录。通配符 是另一种简写形式,用来引用目录中的内容。
例如,假设您的某个目录中包含了 100 个文件。有些是以 .c 为后缀的 C 源代码文件,其他一些是以 .o 为后缀的目标文件,还有一些是文本文件 (.txt)、脚本 (.sh) 和可执行文件(具有执行权限的文件)。要仅列出其中的 C
文件,只需输入:
$ ls *.c
|
通配符 *
(通常称为 star 而不是 asterisk)表示匹配任何字符序列。.c
文件扩展名是一种文本模式,它仅匹配点号加小写字母 c 的情况。所以,*.c
表示任何字符序列加上点号和小写字母 c。在给定了 *.c
之后,Shell 将查看当前目录(除非您提供一个起始绝对或相对路径名),找出所有匹配这个模式的文件名,将 *.c
扩展为文件名列表,然后将这个列表作为参数传递给 ls
命令。
清单 2 基于 wget
的源代码文件演示了 *.c
的使用,wget
是一种命令行的下载实用工具。
|
$ ls *.c
alloca.c
ansi2knr.c
cmpt.c
connect.c
convert.c
...
|
将通配符展开为匹配文件名列表的过程称为通配符匹配 (globbing),并且 UNIX Shell 具有各种各样的通配符匹配操作符(所谓的 glob),以便帮助您描述所要查找的内容:
*
(星号)匹配任何字符或字符序列,包括空序列。
?
(问号)匹配任何单个的字符。
[ ]
(方括号)匹配任何括起来的字符。在方括号中,通过使用 -
(连字符),比如 [a-z]
或者所有的小写字母,您可以引用某个范围的字符。 (Z Shell 具有许多独特的通配符匹配操作符。有关 Z Shell 通配符匹配操作符的更多信息,请参见侧栏。)
您还可以根据需要重复使用通配符匹配操作符。清单 3 提供了一些其他示例。
在清单 3 中,命令 1 显示了该目录中所有的条目,包括长列表中那些以 .(点)开头的条目。(-a
选项显示了所谓的点文件;-1
选项表示在一列中列出所有的内容;而 -F
选项分别使用 /(正斜杠)和 *(星号)突出表示目录和可执行文件。)
命令 2 查找名称以点号开头的条目(即 .*)。第 3 个命令仅查找那些单字母后缀的项目。
第 4 个命令仅查找那些 4 个字母后跟点号和单个字符的项目。最后,命令 5 查找这样的项目:以小写字母 a、b 或 c 开头,后面至少跟一个字母,然后可以是任何内容,接着是点号和任何后缀。正如所看到的,您可以根据实际情况重复使用这些通配符匹配操作符。
1 $ ls -1 -a -F ./libs ChangeLog ChangeLog-branches/ Makefile Makefile.in alloca.c ansi2knr.c cmpt.c cmpt.o config.h config.h.in connect.c connect.h connect.o convert.c convert.h convert.o ... wget* 2 $ ls -a -F .* ./lib 3 $ ls -1 *.? alloca.c ansi2knr.c cmpt.c cmpt.o config.h connect.c connect.h connect.o convert.c convert.h convert.o ... 4 $ ls -1 ????.? cmpt.c cmpt.o 5 $ ls [a-c]?*.* alloca.c ansi2knr.c cmpt.c cmpt.o config.h config.h.in connect.c connect.h connect.o convert.c convert.h convert.o cookies.c cookies.h cookies.o |
那么,ls *.z
将会产生什么样的结果呢(假设不存在这样的文件)?它将产生一条有用的错误消息:
$ ls *.z
zsh: no matches found: *.z
|
|
到目前为止,您已经了解了如何指定路径和选择相应的文件。您可以在命令行中描述需要完成的任务。然而,即使所有的命令行都很短并且很简单,但您仍然有可能对反反复复地输入这些相同的内容而感到厌烦。尤其是,您可能厌倦了输入冗长的、复杂的命令行,其中可能包含大量的选项、或者参数的顺序有严格的要求。幸运的是,大多数 Shell 都维护了以前命令的历史。要再次运行一个命令,只需从这个历史列表中找到相应的条目,然后再次运行它。与 Shell 中其他的部分一样,通过快捷方法可以快速和轻松地进行引用。
要在 Z Shell 中启用命令历史,可以输入:
$ HISTSIZE=500 $ SAVEHIST=500 |
这里的命令指定了 Shell 和持久化历史文件应该保留最后的 500 条命令。(在缺省情况下,Z Shell 仅保存最后的 30 条命令。)有关如何捕获和保存命令历史的信息,请查看您的 Shell 文档。
在 Shell 中进行了一段时间的工作之后,您只需输入 history
就可以查看命令历史:
$ history
...
781 /bin/ls -d */
782 /bin/ls -F *(/)
783 /bin/ls -d -F *(/)
784 /bin/ls -d -F */
785 /bin/ls -d */
|
您所运行的每个命令都会分配到一个顺序的数值标识符。您可以使用这个标识符,如 782,来引用完整的命令和命令中的某些部分。要再次运行一个命令,可以输入 !
(感叹号)加上命令对应的数值:
$ !785
ChangeLog-branches/ doc/ po/ src/ util/ windows/
|
如果您希望从一个历史命令中获得特定的参数,可以使用 !(感叹号)来引用这个命令,并提供 :N,其中 0 表示命令名,1 表示第 1 个参数,依此类推。例如,要提取历史日志中命令 782 的第二个参数,可以输入清单 4 中所示的代码。
$ echo !782:2 echo *(/) ChangeLog-branches doc po src util windows $ ls AUTHORS COPYING INSTALL MACHINES AUTHORS COPYING INSTALL MACHINES $ echo !!:3 echo INSTALL $ history -2 788 ls AUTHORS COPYING INSTALL MACHINES 789 echo INSTALL $ echo !788^ echo AUTHORS AUTHORS $ echo !788$ echo MACHINES MACHINES |
命令 history -2
打印出前两个命令。作为快捷方法,您可以使用 ^
(脱字符号)引用命令的第一个参数(而不是命令名本身),并且您可以使用 $
(美元符号)引用历史命令的最后一个参数。您还可以使用范围符号来引用某个范围的参数,如清单 5 所示。
$ echo AUTHORS COPYING INSTALL MACHINES AUTHORS COPYING INSTALL MACHINES $ echo !!:1-2 echo AUTHORS COPYING AUTHORS COPYING |
还有其他的更直接的方法可以用来再次调用历史命令。其中一种方法是搜索历史命令:
$ ls I* $ ls M* $ echo !?M ls INSTALL |
结构 !?M
寻找最近的包含大写字母 M 的历史命令行。
|
流畅地表达命令行 任务,这是一种基本的 UNIX 技能。但是与 UNIX 进行对话不仅仅只是使用 Shell 提示符,您还必须与各种各样的 UNIX 实用工具进行通信。在 UNIX 中,环境变量保存了 Shell 中的相关设置,并允许您将首选项传播到从命令行启动的所有实用工具中。
有些环境变量称为 Shell 变量,Shell 仅使用这些变量控制其自身的行为。例如,只有 Z Shell 使用 $HISTSIZE
和 $SAVEHIST
管理命令历史,如上所述。可以将 Shell 变量看作相应的设置。
需要对其他的环境变量进行导出、或使得它们全局可用,并将它们复制到从命令行中启动的每个命令的进程空间(即环境)。例如,$HOME
是一个特殊的环境变量,它保存了您的 home 目录的位置。UNIX 登录序列将设置 $HOME
(以及其他的环境变量),然后启动 Shell,而 Shell 反过来使用 $HOME
查找所有的 Shell 启动文件。您所启动的其他应用程序,如 SSH 和 FTP,引用 $HOME
查找 .netrc 文件(用于存储机密的、远程访问的密码)。有些环境变量,如 $HOME
、$PATH
和 $SHELL
,会被所有应用程序使用。其他的环境变量可能专门针对某个应用程序。
要查看当前所有的环境变量,可以输入 printenv
,如清单 6 所示。(根据系统管理员对系统所进行的配置,您系统中的环境变量可能会比本文中所介绍的更多或更少。)
$ printenv
PATH=/Users/strike/bin:/Applications/xampp/xamppfiles/bin:/Users/strike/bin:/usr/bin:/
bin:/usr/sbin:/sbin
HOME=/Users/strike
SHELL=/bin/zsh
USER=strike
TERM=xterm-color
LOGNAME=strike
SHLVL=1
PWD=/Local/src/versions/wget/wget-1.9
OLDPWD=/Local/src/versions/wget/wget-1.9/src
PERL5LIB=/Applications/xampp/xamppfiles/lib/perl5/site_perl/5.8.7:/Projects/IGSP/src
CLICOLOR=true
MANPATH=/Local/root/share/man:/usr/share/man:/opt/local/share/man
INFOPATH=/opt/local/share/info
LESS=-n
|
您可能认识其中大多数的变量,而其他一些可能是新出现的。Shell 级别($SHLVL
)显示您所处的 Shell 的深度。1 表示登录 Shell,2 表示您从登录 Shell 中启动了另一个 Shell,依此类推。您可以使用 $SHLVL
的值来更改后续 Shell(嵌套 Shell)的提示符。$TERM
反映了您的终端(可能是终端模拟程序)设置,对于确保正确地呈现文本、颜色以及对按键进行正确的解释,这是非常重要的信息。$PWD
是您的当前工作目录,而 $OLDPWD
是上一次的工作目录。您可以使用这两个变量实现在两个目录之间的快速切换,如清单 7 所示。
$ echo $PWD /Users/strike $ echo $OLDPWD /Local/src/versions/wget/wget-1.9 $ cd $OLDPWD $ echo $PWD /Local/src/versions/wget/wget-1.9 $ echo $OLDPWD /Users/strike |
上面列表中剩下的环境变量都是应用程序特定的。每个环境变量保存了相应的首选项设置,当您启动了与之关联的应用程序后,它可以用于控制该应用程序的工作方式。$PERL5LIB
是 Perl 查找自定义库的搜索路径。ls
命令使用 $CLICOLOR
通过不同的颜色呈现不同类型的文件(目录为蓝色、可执行文件为绿色,等等)。程序的 man 页面中通常包含对自定义应用程序环境变量的说明。
设置环境变量与设置 Shell 变量的方法相同。然而,您必须导出该变量,以使得它全局可用:
$ MYVARIABLE=$HOME/projectX $ export TMPDIR=/tmp/projectX |
前一个命令设置了名为 $MYVARIABLE
的 Shell 变量。(开头的美元符号是 Shell 提示符。您在设置变量时,不用提供这个 $ 符号。然而,当您使用这个变量时,必须使用美元符号,比如 $MYVARIABLE
。)$MYVARIABLE
仅对 Shell 是可见的,因为没有将其导出。要查看所有 Shell 变量的列表,可以输入 set
。set
的输出包括环境变量,因为它们对 Shell 来说也是可用的。
在后面的一个命令中,设置并导出了 $TMPDIR
,因此它对于从 Shell 中启动的所有应用程序都是可用的。GNU Compiler Collection (GCC) 编译器是一个使用 $TMPDIR
的应用程序。$TMPDIR
中所存储的值表示 GCC 用来存放生成的临时文件的位置。
如果您要删除一个环境变量,只需输入 unset
加上变量名即可,如清单 8 所示。
$ set HOME=/Users/strike MYVARIABLE=/Users/strike/projectX TMPDIR=/tmp/projectX ... $ unset MYVARIABLE TMPDIR $ set HOME=/Users/strike .... |
|
前面的部分主要关注的是如何减少在命令行中的输入。当然,还有许多内容需要学习,因为 Shell 环境非常丰富。然而请记住,功能越强大,生产能力就越大(要对蜘蛛侠说声抱歉,因为修改了原话)。
为了保留以前输入的内容和保存以前的所有设置,UNIX Shell 分别提供了别名和启动文件。别名 是您所创建的快捷方法。每次 Shell 启动时都会读取启动文件,这是保存(和共享)所有 Shell 设置的理想的地方,如 Shell 变量(选项)、环境变量和别名。
别名是一个简短的序列,您可以使用它来代替一个较长的命令。您可以把别名看作是一个命令行的缩写。无需输入:
$ find /home/joe -type f -name '*.txt' -print | xargs grep -l "Monthly Report" |
在命令提示符处,您可以输入已经创建的别名:
$ findreports
|
Shell 减少了工作的复杂程度,它会将 findreports
替换成其扩展形式。要创建 findreports
别名,可以输入:
alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l "Monthly Report"' |
必须使用单引号确定每个别名的界限。如果您需要在别名中使用引号,那么可以使用双引号。Z Shell 别名可以包含许多 Shell 基本单位,包括变量、管道、重定向、其他别名和其他 Shell 操作数,如清单 9 所示。
$ alias ll='/bin/ls -l' $ ll -d 2002* drwxrwxr-x 2 www-data www-data 4096 Jan 16 2002 2002-02 drwxrwxr-x 2 www-data www-data 4096 Jan 22 2002 2002-03 drwxrwxr-x 2 www-data www-data 4096 Apr 15 2002 2002-04 drwxrwxr-x 2 www-data www-data 4096 Apr 19 2002 2002-05 ... $ alias lt='ll -t' $ lt -d 2002* drwxrwxr-x 2 www-data www-data 4096 Apr 19 2002 2002-05 drwxrwxr-x 2 www-data www-data 4096 Apr 15 2002 2002-04 drwxrwxr-x 2 www-data www-data 4096 Jan 22 2002 2002-03 drwxrwxr-x 2 www-data www-data 4096 Jan 16 2002 2002-02 $ alias m='pinky | grep mstreicher' $ m mstreicher Martin Streicher ... $ alias snap='pinky >> ~/.pinky' $ snap $ snap $ cat ~/.pinky Login Name TTY Idle When Where mstreicher Martin Streicher pts/0 Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.com Login Name TTY Idle When Where mstreicher Martin Streicher pts/0 Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.com |
别名 ll
表示 /bin/ls,绝对路径不会被别名替换。
当您输入 ll
时,它会被其别名替换,并且附加上剩下的命令行参数。因此,ll -d 2002*
实际上是命令 /bin/ls -l -d 2002*
。别名 lt
表示 ll
加上 -t
标志,以便根据创建时间进行排序。lt
别名扩展为 /bin/ls -l -t -d 2002*
。m
别名包括一个管道。snap
别名使用重定向将命令的输出附加到文件中。
要查看 Shell 中设置的所有别名,只需输入 alias
(不带参数)即可,如清单 10 所示。
$ alias
alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l
"Monthly Report"'
alias ll='/bin/ls -l'
alias lt='ll -t'
alias m='pinky | grep mstreicher'
alias snap='pinky >> ~/.pinky'
...
|
如果您要删除别名,只需输入 unalias
加上别名的名称即可。您还可以同时列出多个别名,如清单 11 所示。
$ unalias m snap $ alias alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l "Monthly Report"' alias ll='/bin/ls -l' alias lt='ll -t' |
最后,当您经过辛苦的劳动对环境进行了相应的设置之后,您会希望能够保存这些设置,以便下一次继续使用。事实上,您希望 Shell 能够在会话与会话之间、实例与实例之间保持一致,比如当您在工作站上打开了多个终端窗口时。
Shell 包括一些启动文件,当您启动 Shell 时,它们可以用来(重新)初始化您的环境。启动文件可能非常简单(仅包含变量和值的列表),也可能非常复杂(包括自定义逻辑和精心设计的函数)。有些用户会保存多组启动文件,每个项目一组。
Z Shell 使用启动文件 .zshrc 和 .zprofile,这两个文件都位于您的 home 目录中。(其他的 Shell 也具有类似名称的类似文件,您可以阅读 Shell 文档以了解这些细节。有些 Shell 还提供了关闭 文件、或当 Shell 退出时运行的文件。)当启动新的 Shell 时,会将 .zshrc 文件作为信息来源,或对其进行读取和处理;而只有在启动登录 Shell 时才会将 .zprofile 文件作为信息来源。
在您完成对 Shell 的配置之后,请捕获这些设置的快照并将其保存到一个 Shell 启动文件中:
$ set >> $HOME/.zshrc $ alias >> $HOME/.zshrc |
注意:您可能需要编辑所得到的 .zshrc 文件,并删除那些会话特定的变量。
|
噢!本期的对话 UNIX 涉及到了许多基本内容,但是通过实践,您可以得到更多的收获。更灵活地、而不是更辛苦地进行工作,这样可以节省额外的时间来完成一些真正重要的工作,比如玩 Slashem。
下一次,对话 UNIX 将讨论一些守旧的内容。我将放弃那些新潮的浏览器,研究如何在命令行中进行连接、下载、上传、传输和通信。
欢迎阅读下一期文章。
zsh
:阅读该列表以了解更多的 Z shell 技巧和提示。zsh
专业知识。
Martin Streicher 目前担任 Linux Magazine 的主编。他毕业于普渡大学并获得计算机科学硕士学位,从 1982 年起他一直从事类 UNIX 系统的编程工作,他使用的编程语言包括 Pascal、C、Perl、Java 以及近期的 Ruby 语言。您可以通过 与 Martin 联系。 |