您的位置:首页技术文章
文章详情页

对话 UNIX:第 2 部分: 做得多不如做得巧

【字号: 日期:2024-06-17 16:03:01浏览:2作者:猪猪

每种技术性劳动都有其自身的秘密,这些小窍门、技巧和工具甚至可以处理最复杂的任务。例如,我的邻居是一个熟练的木匠。他仅凭眼睛就可以非常精确地测量和改变角度、无缝地进行斜接,并且他所完成的作品为他在当地报纸上赢得了赞誉。

但更神奇的是(至少对于我这样一个肯定会出错的外行来说),他可以相当轻松地完成工作。他干这一行大约有 20 多年了,并且掌握了所有的快捷方法。通过这些快捷方法,可以在这里节省一点点时间,在那里节省一点点劳动,然而对于像进行切割、锤钉子和组装框架这样的重复性任务,这样的节省最终加起来真的不少。

程序员、系统管理员和其他的 Unix® 计算机专业人员都有他们自己专门的工具:

CPU

RAM

操作系统

应用程序

Shell

就像一个经验丰富的木匠,了解一些窍门并应用相应的工具可以节省大量的时间和精力。第 1 期的对话 UNIX 介绍了 UNIX 命令行的强大功能。本文向您介绍一些有用的 Shell 快捷方法,它们有助于您更好地掌握 Shell 提示符。

让您的手指稍事休息,不要让它们过于疲劳

正如第 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 所示。

清单 1. UNIX Shell 中的快捷方法

$ whoamistrike$ 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 符号

针对许多频繁使用的命令行参数,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 是一种命令行的下载实用工具。

单 2. 使用通配符在目录中查找 C 源代码文件

$ ls *.calloca.cansi2knr.ccmpt.cconnect.cconvert.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 开头,后面至少跟一个字母,然后可以是任何内容,接着是点号和任何后缀。正如所看到的,您可以根据实际情况重复使用这些通配符匹配操作符。

清单 3. 通配符示例

1 $ ls -1 -a -F./libsChangeLogChangeLog-branches/MakefileMakefile.inalloca.cansi2knr.ccmpt.ccmpt.oconfig.hconfig.h.inconnect.cconnect.hconnect.oconvert.cconvert.hconvert.o...wget*2 $ ls -a -F .*./lib3 $ ls -1 *.?alloca.cansi2knr.ccmpt.ccmpt.oconfig.hconnect.cconnect.hconnect.oconvert.cconvert.hconvert.o...4 $ ls -1 ????.?cmpt.ccmpt.o5 $ ls [a-c]?*.*alloca.cansi2knr.ccmpt.ccmpt.oconfig.hconfig.h.inconnect.cconnect.hconnect.oconvert.cconvert.hconvert.ocookIEs.ccookies.hcookies.o

那么,ls *.z 将会产生什么样的结果呢(假设不存在这样的文件)?它将产生一条有用的错误消息:

$ ls *.zzsh: 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,来引用完整的命令和命令中的某些部分。要再次运行一个命令,可以输入 !(感叹号)加上命令对应的数值:

$ !785ChangeLog-branches/ doc/ po/ src/ util/ Windows/

如果您希望从一个历史命令中获得特定的参数,可以使用 !(感叹号)来引用这个命令,并提供 :N,其中 0 表示命令名,1 表示第 1 个参数,依此类推。例如,要提取历史日志中命令 782 的第二个参数,可以输入清单 4 中所示的代码。

清单 4. 提取命令 782 的第二个参数

$ echo !782:2 echo *(/)ChangeLog-branches doc po src util windows$ ls AUTHORS COPYING INSTALL MacHINESAUTHORS  COPYING  INSTALL  MACHINES$ echo !!:3echo INSTALL$ history -2788 ls AUTHORS COPYING INSTALL MACHINES789 echo INSTALL$ echo !788^echo AUTHORSAUTHORS$ echo !788$echo MACHINESMACHINES

命令 history -2 打印出前两个命令。作为快捷方法,您可以使用 ^(脱字符号)引用命令的第一个参数(而不是命令名本身),并且您可以使用 $(美元符号)引用历史命令的最后一个参数。您还可以使用范围符号来引用某个范围的参数,如清单 5 所示。

清单 5. 范围符号

$ echo AUTHORS COPYING INSTALL MacHINESAUTHORS COPYING INSTALL MACHINES$ echo !!:1-2echo AUTHORS COPYINGAUTHORS COPYING

还有其他的更直接的方法可以用来再次调用历史命令。其中一种方法是搜索历史命令:

$ ls I*$ ls M*$ echo !?Mls 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 所示。(根据系统管理员对系统所进行的配置,您系统中的环境变量可能会比本文中所介绍的更多或更少。)

清单 6. 查看环境变量

$ printenvPATH=/Users/strike/bin:/Applications/xampp/xamppfiles/bin:/Users/strike/bin:/usr/bin:/bin:/usr/sbin:/sbinHOME=/Users/strikeSHELL=/bin/zshUSER=strikeTERM=xterm-colorLOGNAME=strikeSHLVL=1PWD=/Local/src/versions/wget/wget-1.9OLDPWD=/Local/src/versions/wget/wget-1.9/srcPERL5LIB=/Applications/xampp/xamppfiles/lib/perl5/site_perl/5.8.7:/Projects/IGSP/srcCLICOLOR=trueMANPATH=/Local/root/share/man:/usr/share/man:/opt/local/share/manINFOPATH=/opt/local/share/infoLESS=-n

您可能认识其中大多数的变量,而其他一些可能是新出现的。Shell 级别($SHLVL)显示您所处的 Shell 的深度。1 表示登录 Shell,2 表示您从登录 Shell 中启动了另一个 Shell,依此类推。您可以使用 $SHLVL 的值来更改后续 Shell(嵌套 Shell)的提示符。$TERM 反映了您的终端(可能是终端模拟程序)设置,对于确保正确地呈现文本、颜色以及对按键进行正确的解释,这是非常重要的信息。$PWD 是您的当前工作目录,而 $OLDPWD 是上一次的工作目录。您可以使用这两个变量实现在两个目录之间的快速切换,如清单 7 所示。

清单 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 所示。

清单 8. 删除环境变量

$ setHOME=/Users/strikeMYVARIABLE=/Users/strike/projectXTMPDIR=/tmp/projectX...$ unset MYVARIABLE TMPDIR$ setHOME=/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 所示。

清单 9. Z Shell 基本单位

$ alias ll='/bin/ls -l'$ ll -d 2002*drwxrwxr-x 2 www-data  www-data4096 Jan 16 2002 2002-02drwxrwxr-x 2 www-data  www-data4096 Jan 22 2002 2002-03drwxrwxr-x 2 www-data  www-data4096 Apr 15 2002 2002-04drwxrwxr-x 2 www-data  www-data4096 Apr 19 2002 2002-05...$ alias lt='ll -t'$ lt -d 2002*drwxrwxr-x 2 www-data www-data 4096 Apr 19 2002 2002-05drwxrwxr-x 2 www-data www-data 4096 Apr 15 2002 2002-04drwxrwxr-x 2 www-data www-data 4096 Jan 22 2002 2002-03drwxrwxr-x 2 www-data www-data 4096 Jan 16 2002 2002-02$ alias m='pinky | grep mstreicher'$ mmstreicher Martin Streicher ...$ alias snap='pinky >> ~/.pinky'$ snap$ snap$ cat ~/.pinkyLoginNameTTY Idle  When Wheremstreicher Martin Streicherpts/0Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.comLoginNameTTY Idle  When Wheremstreicher Martin Streicherpts/0Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.com

标签: Unix系统