学习bash第二版-第一章 bash基础

  自从20世纪70年代早期UNIX操作系统创建以来,它越来越流行。这段时间内它衍生出各种不同的版本,如Ultrix、AIX、Xenix、SunOS和Linux。从微型机和大型机开始,它已经移向了桌面工作站,甚至是工作和家用的个人计算机。UNIX系统也不只用于大学和研究中心的计算,它还用于许多商务机构、学校和家庭。随着时间的推移,将会有越来越多的人接触到UNIX。
  你可能在学校、办公室或家里使用过UNIX运行应用程序、打印文件和阅读电子邮件,但当键入一个命令并按RETURN时,你想过所发生的过程吗?
  当你键入一个命令时,发生了许多层次的事件,但我们只考虑上层的东西,称为shell。一般的讲,shell是UNIX系统的用户接口。例如,它可以是任何一个接受用户输入,并将之翻译成操作系统可以理解的指令,并把操作系统的输出结果返回给用户的程序。图1-1显示了用户、shell和操作系统之间的关系。
  存在各种类型的用户接口,bash属于其中最常用的,称为基于字符的用户接口。此类接口接受用户键入的文本命令行,并且通常产生基于文本的输出。其他类型的接口包括日益流行的常用图形用户接口(GUI),它增加了显示任意图形(不只是键入的字符)并从鼠标或其他点阵设备、触屏接口(例如银行的取款机)等中接受输入的能力。
**shell简介
  shell的任务是将用户命令行翻译成操作系统指令。例如,下面的命令行:
  sort -n phonelist > phonelist.sorted
  意思是“按数值次序将文件phonelist按行分类,并把结果放入文件phonelist.sorted中”。下面是shell对此命令所执行的任务:
  1.把上面一行分成片sort,-n,phonelist,>和phonelist.sorted,这些片称为关键字。
  2.判断关键字的意图:sort是一个命令,-n和phonelist是参数,>和phonelist.sorted结合起来是I/O指令。
  3.依据> phonelist.sorted(输出到文件phonelist.sorted)和一些标准的隐含指令设置I/O。
  4.在一个文件中找到命令sort,使用选项-n(数值次序)和参数phonelist(输入文件)运行它。
  当然每一步骤实际上都包含几个子步骤,其中每个子步骤包含了针对底层操作系统的特定指令。
  记住,shell本身不是UNIX-只是其用户接口。UNIX是用户接口独立于操作系统的操作系统之一。
**本书范围
  本书将介绍bash,它是最流行、功能最强大的主流UNIX shell之一。使用bash有2种方式:作为用户接口和作为编程环境。
  本章和下一章介绍交互式用法。这两章将介绍使用shell完成大部分日常工作所需的背景知识。
  使用shell一段时间后,毫无疑问你会想要改变环境(shell的外观)中的某些字符以及使某些任务自动化。第三章将介绍具体实现方式。
  第三章“定制用户环境”也为shell编程做了准备。在第四章“基础shell编程”到第六章“命令行选项和键入变量”中则详细介绍了shell编程,不必有任何编程经验就可以理解这些章节,学习shell编程。第七章“输入/输出和命令行处理”和第八章“进程处理”详细讲解了shell I/O和进程处理功能。第九章“调试shell程序”讨论了各种调试shell程序的技术。
  通过本书你会学到许多有关bash的内容,还会学到UNIX工具盒UNIX操作系统通常的工作方式。即使以前没有任何编程经验,你也可以成为一个shell编程的高手。同时,我们尽量避免涉及过多的UNIX内核细节。因为并非是内核专家才能使用并编制有效地shell。书中也不纠缠于专门面向底层系统程序员的一些shell特性。
**UNIX shell历史
  shell独立于UNIX操作系统根本上导致了UNIX历史上各种shell的发展-但只有几种得到了广泛使用。
  第一个主要的shell是Bourne shell(以其发明者Steven Bourne命名),它包含在UNIX第一个流行版本版本7中,始于1979年。Bourne shell在系统上称为sh,虽然UNIX已经经过了许多变化,Bourne shell仍很流行,基本没变。几种UNIX功能和管理特性都建立在Bourne shell之上。
  第一个广泛流行的可替代shell产品是C shell,即csh。它由Berkeley的California大学的Bill Joy编写,版本7出台两年后在UNIX的Berkeley软件发布(BSD)版本出现,并被包含在大多数当前的UNIX版本中。
  C shell的名称来源于其语句命令与C程序语言的相似,这使得UNIX系统上的编程人员更容易学习shell。它支持许多原来BSD UNIX所独有但现在已被其他UNIX版本所支持的操作系统特性(例如作业控制,详细内容请参见第八章)。它还有一些极易使用的重要特性(例如别名,详细内容请参见第三章)。
  最近几年许多其他shell开始流行,其中最引人注目的是Korn shell。这个shell是结合了Bourne shell和C shell最佳特性,并加入了许多自身特性的一个商业产品。Korn shell在许多方面类似于bash,两者都有大量易于协同工作的特性。bash的优势在于它是免费的,关于Korn shell的详细信息请参见附录一。
**Bourne Again shell
  Bourne Again shell(暗示其来源于Steve Bourne的shell)创建用于GNU项目。GNU项目由Free Software Foundation(FSF)的Richard Stallman始创,用于创建一个UNIX兼容的操作系统,并使用该免费的发布版本替代所有的商业UNIX版本。GNU不但结合了新的软件功能,而且给出一种新的发布概念:copyleft。只要没有对软件的进一步发布加以限制,copyleft的软件即可自由发布(例如,可免费使用源代码)。
  bash意指GNU系统的标准shell,正式发布于1988年11月10日。Brain Fox编写了bash的最初版本及解释说明,并且直到1993年仍在不断加以改进。1989年初,Chet Ramey也加入进来,负责大量的故障调试及加入许多有用的特性。Chet Ramey现在是bash的正式所有者,并不断对其进行升级。
  根据GNU原则,可免费从FSF得到0.99版本以上的所有bash版本。bash已经覆盖了所有主要的UNIX版本,并迅速成为最流行的Bourne shell派生物。它是广泛免费使用的UNIX操作系统Linux所用的标准shell。
  在1995年,Chet Ramey开始致力于新版本2.0,它第一次公开发布于1996年12月23日。bash 2.0加入了老版本(最后一个是1.14.7)中没有的许多新特性,并使shell与各种标准之间具有更好的兼容性。
  本书将介绍bash 2.0的最新版本(版本2.01,发布于1997年)。它可用于所有旧的bash版本。当前版本中与以前版本不同的特性,或旧版本中所没有的新特性都会在文本中注释。
**Bash特性
  虽然Bourne shell仍被看作标准shell,但bash已越来越流行。除了其与Bourne shell的兼容性,它还包含了C shell和Korn shell的最佳特性,以及本身的一些优势。
  bash的命令行编辑模式是首先吸引人的特性。有了命令行编辑,它就更容易回馈和定位错误或修改以前的命令,此特性比C shell的历史机制更方便,并且Bourne shell还没有该功能。
  其他交互式用户最常用的重要bash特性是作业控制。正如第八章中将要介绍的,作业控制使你可以同时停止、启动和暂停任意数目的命令。此特性几乎是照搬C shell。
  另外,bash的重要优势还在于,它主要是针对shell定制者和程序员的。他有许多用于定制的新选项和变量,其可编程特性已经扩展到函数定义、更多的控制结构、整数运算、高级I/O控制等。
**得到bash
  你可能不能马上使用bash。你的系统管理员可能将你的账号设置为使用系统上的“标准”shell,你甚至不知道还有没有其他shell可用。
  判断你所使用的shell很容易,登录系统,在提示符下键入echo $SHELL,将出现sh、csh、ksh或bash的结果。它们分别表示Bourne shell、C shell、Korn shell和bash shell(你也可能在使用其他的shell,如tcsh)。
  如果你没有使用过bash,而现在要使用它,那么首先需要找出他是否存在于你的系统中。键入bash,如果得到一个包含一些信息和美元符号(如bash2-2.01$)的新提示符,则一切就没问题,键入exit返回到正常shell。
  如果得到信息为“未发现”,表明系统中没有bash。那么请教你的系统管理员或其他资深用户;有可能系统某位置(目录)下安装了某版本的bash,但正常情况下你访问不到。如果不是这样,请参看第十一章以了解如何才能获得一个bash版本。
  一旦知道系统上存在bash看,就可以从所使用的任何其他shell调用它,方法是键入bash。然而,将其注册为登录shell会更好一些,也就是登录时自动获得的shell。可以根据下面的介绍自己动手执行此过程。如果哪个操作不能正常完成(例如,键入一个命令,得到错误信息“未找到”,或以空行响应),则必须退出该过程。查看系统管理器,或是参见第十一章,在那里我们阐述了替换当前shell的更为直接的方式。
  你需要找出bash在系统上的位置,例如,它被安装的目录。方法是键入whereis bash(特别是如果你正在使用C shell);如果这样不行,再试试whence bash、which bash或下面的复杂命令:
  grep bash /etc/passwd | awk -F: '{print $7}' | sort -u
  应该会看到像/bin/bash或/usr/local/bin/bash这样的响应。
  要将bash安装为登录shell,键入chsh bash-name,这里bash-name是从whereis命令得到的响应(正常情况下),例如:
  chsh /usr/local/bin/bash
  可能会得到一个错误信息显示shell无效,或是提示要输入口令。键入口令,然后注销并再次登录以启动bash。
**交互式shell用法
  当交互的使用shell时,用户登录后就会处于会话模式下,可以通过键入exit、logout或按CTRL-D退出。在登录会话期间,可以向shell输入命令行,它们是一些在终端或工作站上键入的以RETURN结束的文本行。
  默认情况下,shell对命令的提示是一个信息字符串后跟一个美元符号,如第三章将要介绍的,整个提示符都可被改变。
**命令、参数和选项
  shell命令行由一或多个单词组成,它们由命令行上的空格或TAB键分隔。行上的第一个单词是命令,其余的(如果有)是命令参数,这些参数是命令将对其实施操作的事物名称。
  例如,命令行lp myfile由命令lp(打印一个文件)和单个参数myfile组成。lp将myfile看做要打印的文件名。参数通常是文件名,但不是必须是文件名。在命令行mail cam中,mail程序将cam看做信息将被发送的用户名。
  选项是一种特殊参数,它给出命令要采取的动作的特定信息。选项通常由一个短划线后跟字母组成;这里说通常是因为这是一种习惯用法而不是一个硬性规则。命令lp -h myfile包含了选项-h,它通知lp在打印文件前不打印标志页。
  有时选项本身也带有参数。例如,lp -d lp1 -h myfile有两个选项和一个参数,第一个选项是-d lp1,意思是“发送输出到打印机(目标)lp1”,第二个选项和参数与前面例子的一样。
**文件
  虽然命令参数不总是文件,文件却是任何UNIX操作系统中最重要的事物。一个文件可能包含各种信息,实际上也有各种类型的文件。目前最重要的三类文件是:
  普通文件
  也称文本文件,包含了可读的字符。例如,本书创建于几个普通文件,包含了本书的文本和troff文字处理器所用的可读格式化指令。
  可执行文件
  也称为程序,可作为命令调用。其中一些人无法阅读,其他的则是特殊的文本文件,如本书给出的shell脚本。shell本身就是一个称为bash的(人无法阅读)可执行文件。
  目录
  类似于包含其他文件或其他目录(称为子目录)的文件夹。
**目录
  下面介绍有关目录的最重要的概念。由于一个目录可以包含其他目录,因此产生了分层结构,UNIX系统中所有的文件构成树。
  图1-2显示了一个典型的目录树;矩形是目录,椭圆是普通文件。
  树的顶端是一个称为根的目录,在系统上没有名字。所有文件名在系统上都可通过相对于根的位置表达。这些文件名由所有目录名(从根开始)组成,由斜线符号(/)分隔,后跟文件名。这种命名文件的方式称为全(或绝对)路径名。
  例如,假如文件aaiw在目录book下,book在cam下,cam在home下,home在根下,则此文件的全路径名为/home/cam/book/aaiw。
**工作目录
  当然,在指定一个文件时使用全路径名是很麻烦的。因此,还有一个工作目录的概念(有时称为当前目录),它是你在任意给定时间所在的目录。如果给出一个路径名不带前斜线,那么该文件的位置就是相对工作目录的。这样的路径名称为相对路径名,比起全路径名,使用它们更频繁。
  当登录一个系统时,用户的工作目录被初始化为一个指定目录,称为用户主(home)目录,也称登录目录。系统管理员在建立系统时常使每个人的主目录名与其登录名相同,并且所有的用户主目录都包含在根的一个常用目录下。
  例如,/home/cam是典型的用户主目录。如果这是你的工作目录,那么当给出命令lp memo时,系统会在/home/cam下查找文件memo。如果在主目录下有一个目录hatter,它包含文件teatime,则可以使用命令lp hatter/teatime打印它。
**~标记
  主目录经常出现在路径名里,尽管许多系统中所有的主目录都有一个通用的父目录(如/home或/users),你也不应该依赖于这种情况,甚至可以不必知道主目录的绝对路径名。
  因此,bash有一种缩略主目录的方式:在用户名前加~符号。例如,可以使用~alice/story引用alice主目录下的文件story。这是绝对路径名,因此与当前所在的路径无关。如果alice的根目录下有一个子目录adventure,story在该目录下,则可以使用~alice/adventure/story作为其名字。
  更方便的是,~符号本身可引用用户自己的主目录。可以使用~/notes引用主目录下的文件notes(注意其与~notes的差别,shell会试图将后者解释为用户notes的主目录)。如果notes在adventure子目录下,那么可以使用~/adventure/notes。该标记在工作目录不是主目录的情况下使用起来很方便。例如,现在工作目录在系统目录/tmp下。
**改变工作目录
  如果要改变工作目录,可以使用命令cd。如果你不记得自己当前的工作目录,则可以使用命令pwd打印它。
  cd接受你要改变为工作目录的目录名为参数。它可以是相对当前目录的,也可以包含~标记或为绝对路径(以斜线开始)。如果省略参数,使用cd将改变到用户主目录(使用cd~也一样)。
  表1-1列出cd命令示例。每个命令都假定该命令被执行前工作目录为/home/cam。目录结构如图1-2所示。
  
  表1-1 cd命令示例
  命令                   新的工作目录
  cd book                /home/cam/book
  cd book/wonderland     /home/cam/book/wonderland
  cd ~/book/wonderland   /home/cam/book/wonderland
  cd /usr/lib            /usr/lib
  cd ..                  /home
  cd ../gryphon          /home/gryphon
  cd ~gryphon            /home/gryphon
  
  前4个命令很直接,之后的2个命令使用了特殊目录..(两个点),它表示“当前目录的父目录”。每个目录都有一个父目录,它是进入目录层次中当前目录的上一级目录[称为父目录]的通用方式。
  bash的cd命令的另一特性是cd -,它改变到前一个工作目录。例如,如果开始在/usr/lib目录中,键入不带参数的cd进入主目录,然后键入cd -则返回到目录/usr/lib。
**文件名、通配符和路径名扩展
  有时需要一次运行具有多个文件的命令,最常见的例子是ls命令,它列出文件信息。其最简单形式不带选项和参数,列出工作目录下除了名字以点号(.)开始的特殊隐藏文件外其他所有文件名。
  如果向ls给出文件名参数,则列出这些文件,但没有分类信息:如果当前目录下有文件duchess和queen,键入ls duchess queen,系统将单独打印这些文件名。
  实际上经常使用的是带选项的ls,这些选项告诉ls列出文件的信息。如-l选项,使用该选项列出文件的所有者、大小、最后修改时间和其他信息。使用-a(所有)选项则将同时列出隐藏文件的信息。但有时你只想验证一组文件存在,则不需要知道其所有的文件名。例如,如果使用一个文本编辑器,可能要查看当前目录下哪些文件以.txt结尾。
  文件名在UNIX中十分重要,因此shell提供了一种内置方式,可以指定在不需要知道所有文件名的情况下的文件名集合模式,可以使用称为通配符的特殊字符把文件名加入到模式中。表1-2列出基本的通配符。
  
  表1-2 基本通配符
  通配符     匹配
  ?          任意单个字符
  *          任意字符字符串
  [set]      set中任意字符
  [!set]     不在set中的任意字符

  ?通配符匹配任意单个字符。因此如果目录中包含文件program.c、program.log和program.o,那么表达式program.?匹配program.c和program.o,但不匹配program.log。
  星号(*)功能更强大,用途更广泛。它匹配任意字符串。表达式program.*将匹配前面例子中所有三个文件,对于文本编辑器,可以使用表达式*.txt匹配其输入文件。
  表1-3说明了星号的工作方式,这里假定工作目录下有文件bob、darlene、dave、ed、frank和fred。
  
  表1-3 使用星号通配符
  表达式     结果
  fr*        frank fred
  *ed        ed fred
  b*         bob
  *e*        darlene dave ed fred
  *r*        darlene frank fred
  *          bob darlene dave ed frank fred
  d*e        darlene dave
  g*         g*
  
  注意,*可表示空,*ed和*e*都匹配ed。最后一个例子显示了不匹配任何内容时shell给出的结果,保留带有通配符的字符串不变。
  剩下的一个通配符是set结构。set是字符列表(例如,abc)、范围(例如,a-z)或两者的结合。如果你要短划线字符成为列表的一部分,在开始或最后列出它即可。表1-4给出了清晰的解释。
  
  表1-4 使用set结构通配符
  表达式          匹配
  [abc]           a、b或c
  [.,;]           句点、逗号或分号
  [-_]            短划线或下划线
  [a-c]           a、b或c
  [a-z]           所有小写字母
  [!0-9]          所有非数字
  [0-9!]          所有数字和惊叹号
  [a-zA-Z]        所有小写和大写字母
  [a-zA-Z0-9_-]   所有字母、数字、下划线和短划线
  
  在上述通配符例子中,program.[co]和program.[a-z]都匹配program.c和program.o,但不匹配program.log。
  左括号后面的惊叹号表示“否定”一个集合。例如,[!.;]匹配除句点和分号外的任意字符。[!a-zA-Z]匹配非字母的任意字符。要匹配!本身,将其放在集合中第一个字符后面或在其前面加上一个反斜线,如[\!]。
  范围标记很方便,但不应对字符包含的范围做过多假设。使用大写字母、小写字母、数字或其中任意子集都是安全的(例如,[f-q],[2-6])。不要对标点字符或混合大小写字母使用范围,如[a-Z]和[A-z]并不一定会包含所有字母,因为这样的范围在各种不同类型的计算机之间不完全统一。
  文件名包含通配符的表达式的匹配过程称为通配符扩展或聚集,它是shell在读取和处理一个命令行时采取的几个步骤之一。已经介绍过的还有~符号扩展,这里~符号被替换为所应用的根目录。后面几章还会介绍一些,关于该过程的完整信息请参见第七章。
  然而,重要的是要知道运行的命令只会看到通配符扩展的结果,也就是说,这些命令只看到参数的列表,它们并不知道这些参数如何形成。以前面的文件为例,如果键入ls fr*,那么shell会将命令扩展为ls fred frank并调用带有参数fred和frank的ls命令。如果键入ls g*,则(因为没有相应的匹配)传给ls的是字符串g*,并给出错误信息g*:No such file or directory。
  下面给出一个例子,它使问题更加清晰。假定你是一个C程序员,这意味着你要处理的文件名结尾为.c(程序,也称为源文件)、.h(程序头文件)和.o(不可读的目标代码文件)。假如要列出工作目录下所有的源文件、目标文件和头文件,使用命令ls *.[cho]就可以了。shell扩展*.[cho]为所有名字以一个句点后跟c,h或o结尾的文件,并把结果列表作为参数传递给ls命令。换句话说,ls会把这些文件名看做它们好像是被单独键入的。但注意,我们根本不需要知道任何实际的文件名。使用通配符就可以。
  目前介绍过的通配符例子实际上都是称为更一般意义上的路径名扩展的一部分。就像对当前目录使用通配符一样,它们也可以作为路径名的一部分。例如,如果要列出目录/usr和/usr2下的所有文件,可以键入ls /usr*。如果你只对这些目录下以字母b和e开头的文件感兴趣,可以键入ls /usr*/[be]*列出它们。
**大括号扩展
  与路径名扩展很相近的概念是大括号扩展。路径名扩展通配符会扩展为已存在的文件名和路径名,大括号扩展则扩展为给定形式的任意字符串:一个可选的前缀(preamble)后跟大括号内用逗号分隔的字符串,再跟一个可选的后缀(postscript)。如果键入echo b{ed,olt,ar}s,会打印出单词beds、bolts和bars。大括号内每个字符串实例都结合了前缀b和后缀s。注意,它们不是文件名-产生的字符串与文件名无关。大括号还可以嵌套,如b{ar{d,n,k},ed}s。扩展结果是bards、barns、barks和beds。
  大括号扩展也可被通配符扩展使用。在前面一节的例子中,我们列出了工作目录下的源文件、目标文件和头文件,也可以使用ls *.{c,h,o}。
**输入和输出
  软件业,实际上所有科学行业,都相对下面一种情况做出迅速的反应:某人(亦即不是一个组织)提出一个思想,概念虽小,但其应用前景巨大。UNIX的标准输入和输出策略就是这样一种思想的产物,并伴随着诸如LISP语言、关系型数据模型和面向对象编程的创新。
  UNIX I/O策略基于两个显著的简单思想。首先,UNIX文件I/O采用任意长字符(字节)的形式。相反,旧版本的文件系统采用更为复杂的I/O策略(例如,“块”、“记录”、“卡片图形”等)。第二,系统上无论是产生还是接受数据的设备都当做文件对待,这包括如磁盘驱动器和终端这样的硬件设备。旧系统则分别对待每个设备。这两个思想概念使许多系统程序员的工作更加轻松。
**标准I/O
  按惯例,每个UNIX程序都有一种称为标准输入的接受输入的方式、称为标准输出的产生输出的方式,以及称为标准错误输出(通常缩写为标准错误)的产生错误信息的方式。当然,一个程序也会有其他输入和输出源,具体内容见第七章。
  标准I/O是专门针对终端的交互式用户的创新策略,而不再是针对那些打孔卡片的一种旧风格。因为UNIX shell提供了用户的交互,所以标准I/O的设计理所当然会与shell非常吻合。
  所有shell处理标准I/O的方式基本相同,每个调用的程序都有三种到终端或工作站的标准I/O通道设置,因此标准输入就是键盘,标准输出和错误就是屏幕或窗口。例如,mail实用程序打印信息到标准输出设备中,当你使用它发送信息给其他用户,它从标准输入设备中接受输入。这表明你可以在屏幕上查看信息,并在键盘上键入新信息。
  必要时,可以重定向输入和输出使其来自或送到一个文件。如果要以邮件方式发送一个已经存在的文件的内容给某人,可以重定向mail的标准输入以便它从文件而不是键盘上读取信息。
  还可以将程序结合在一个管道内,在管道内一个程序的标准输出直接连接另一个程序的标准输入。例如,你可以让mail直接输出到lp程序,这样信息将被打印而不是显示在屏幕上。
  这使得使用UNIX实用程序可以像堆砌模块一样构建大程序。许多UNIX实用程序就是如此:它们分别对输入的文本执行某种类型的过滤操作。虽然这不是一本关于UNIX实用程序的教科书,但它们对有效的使用shell是非常重要的。目前常用的过滤功能如表1-5所示。
  
  表1-5 常用UNIX数据过滤功能
  功能     意图
  cat      将输入复制到输出
  grep     在输入中检索字符串
  sort     在输入中将行排序
  cut      在输入中抽取列
  sed      对输入执行编辑操作
  tr       将输入字符转换成其他字符
  
  你以前可能用过某些操作,注意到它们是以输入文件名做参数,并将结果输出到标准输出设备上。然而,你可能不知道,如果省略了参数,它们(及大部分UNIX实用程序)就会从标准输入中接受输入。
  例如,最基本的功能cat,它将输入直接复制到输出。如果键入cat加文件名,将会在屏幕上打印出该文件的内容。但如果不带参数调用它,则其等待标准输入,并将其复制到标准输出。自己可以试一下:cat会等待你键入一行文本,当你键入RETURN时,cat会在屏幕上重复该文本。要停止该过程,在行开始时键入CTRL-D,这时将出现^D。下面为该过程:
  $ cat
  Here is a line of text.
  Here is a line of text.
  Here is another line of text.
  Here is another line of text.
  ^D
  $
  
**I/O重定向
  cat是“catenate”的缩写,意思是连接在一起。它接受多个文件名参数,并将它们复制到标准输出。但现在让我们假设cat和其它功能不接受文件名参数,只从标准输入设备中接受输入,正如上面所说,shell允许你重定向标准输入以便它可以来自一个文件。command<filename的功能即如此,但它们将命令设置为但从一个文件而不从终端接受标准输入。
  例如,如果有一个包含某文本的文件cheshire,则cat<cheshire将在终端上打印出cheshire的内容。sort<cheshire将cheshire的行排序,并将结果打印在终端(这里假定所有功能均不带文件名参数)。
  类似的,command>filename使得命令的标准输出重定向到命名文件。最典型的例子是date>now,date命令将当前时间和日期打印到标准输出;而>命令将其保存到文件now中。
  输入和输出重定向可以结合起来。例如,cp命令用于复制文件,如果由于某种原因它不存在或被破坏,可以以下面的方式使用cat命令:
  $ cat < file1 > file2
  上述命令类似于cp file1 file2。
**管道行(pipeline)
  也可以将一个命令的输出重定向到另一个命令的标准输入而不是一个文件。这种结构称为管道(pipe),用|表示,包括与管道连接的两个或多个命令的命令行称为管道行。
  more命令常使用管道,它的功能类似于cat,分屏打印其输出,用户键入SPACE时进入下一屏,RETURN进入下一行。如果所在的目录拥有大量的文件,如果要查看其细节,可以使用ls -l | more一次显示一屏的给出这些文件的详细列表。
  管道行可以很复杂,它们也可以结合其他的I/O重定向。要分屏查看文件cheshire的排序列表,键入sort < cheshire | more;要打印它而不是在终端上查看,键入sort < cheshire | lp。
  下面的例子更复杂。文件/etc/passwd保存UNIX系统上的用户账号信息,文件中的每行包含了用户的登录名、用户ID号、加密的密码、根目录、登录shell和其他信息,每行的第一个域是登录名,各域间以冒号(:)分隔。示例行如下:
  cam:LM1c7GhNesD4GhF3iEHrH4FeCKB/:501:100:Cameron Newham:/home/cam:/bin/bash
  要得到系统上所有用户的分类列表,应键入:
  $ cut -d: -f1 < /etc/passwd | sort
  (实际上可以省略<,因为cut允许输入文件名参数)。cut命令从输入域中抽取用冒号(-d:)分隔的第一个域(-f1),整个管道行将打印下面列表:
  adm
  bin
  cam
  daemon
  davidqc
  ftp
  games
  gonzo
  ...
  如果要将列表直接发送给打印机(而不是屏幕),用如下管道行替代:
  $ cut -d: -f1 < /etc/passwd | sort | lp
  I/O重定向和管道就是这样支持UNIX构建块的体系的。它们非常简明,但功能强大。管道概念非常重要,它消除了对在将其导入其他命令前存储命令输出造成的凌乱的临时文件。
  例如,要在其他操作系统上执行与上述命令行相同的排序操作(假定等价的功能可利用)需要三个命令,在DEC的VAX/VMS系统上,这些命令可能如下:
  $ cut [etc]passwd /d=":" /f=1 /out=temp1
  $ sort temp1 /out=temp2
  $ print temp2
  $ delete temp1 temp2
  多尝试一下,你就会习惯键入强大的管道行,它只需一行就可以完成其他一些操作系统上需要几条命令(和临时文件)才能做到的事情。
**后台作业
  管道实际上是一种普通特性的特例:一次做很多操作。这是一种许多其他商业操作系统所没有的功能,因为它们对用户施加了严格的限制。而从另一角度说,UNIX是在研究领域发展,并被内部用户使用,因此它对计算机上用户可利用资源的限制相对较少-通常,学习起来很简单、规范,而不过分复杂化。
  “一次做很多操作”意味着同时运行多个程序,调用管道行时就是这样;还有就是可以同时随意多次登录一个UNIX系统(例如,如果在一个IBM的VM/CMS系统上执行,就会得到一个讨厌的“已经登录”的信息)。
  shell还允许在单个会话期间一次运行多个命令。正常情况下,当键入一个命令并按下RETURN时,shell将在命令运行结束前使其一直保持对终端的控制权;在第一个命令结束前不能键入下一个命令,但如果要运行一个无需用户输入的命令,并且用户在命令执行期间想做其他事情,则可以在命令后加符号&。
  这被称为在后台运行命令,以这种方式运行的命令称为后台作业;比较而言,正常方式运行的作业称为前台作业。当启动一个后台作业时,会立即得到shell提示符,使你可以输入其他命令。
  后台作业最明显的用法是对那些运行时间长的程序,诸如对大文件的sort或uncompress。例如,假定有一个从磁带中导入到某目录的巨大的压缩的文件,称该文件为gcc.tar.Z,它是一个包含了超过10MB的源代码文件的压缩文件。
  键入uncompress gcc.tar &(可以省略.Z),系统会后台启动一个作业解压数据,并最后给出一个文件gcc.tar。键入该命令后,会看到下面一行:
  [1] 175
  后面跟shell提示符,意味着可以输入其他命令了。该数字指定了引用后台作业的方式,详细内容请参见第八章。
  可以用命令jobs检验后台作业。对每个后台作业,jobs打印出类似上面的一行代码,但带有作业状态的提示信息:
  [1]+  Running                 uncompress gcc.tar &
  作业完成时,可以在shell提示符下看到下面类似信息:
  [1]+  Done                    uncompress gcc.tar
  如果后台作业以错误终止,该信息会有所不同,细节也请参见第八章。
**后台I/O
  放在后台的作业不应该对终端进行I/O操作,仔细思考,你就会明白其中的原因。
  根据定义,一个后台作业对终端没有控制权。这表明只有前台进程(如果没有,就是shell本身)会监听键盘的输入。如果后台作业需要键盘输入,它将空等,直至你给出其输入(详细内容请参见第八章)。
  如果后台作业产生了屏幕输出,输出就会出现在屏幕上。如果你同时在前台运行一个产生输出的作业,那么两个输出会随意出现(这会产生混乱)。
  如果要运行一个后台作业,并需要给出标准输入或产生输出,通常要重定向I/O以便其可以从一个文件接受输入或向一个文件输出。产生一行小信息(警告、“done”信息等)的程序是规则的一个例外;有时你会发现这些信息夹杂在其他输出中也很正常。
  例如,diff功能检验两个文件,参数是两个文件名,结果是在标准输出上打印出它们的不同。如果文件完全一样,diff结果为空。通常,调用diff是要发现其中的不同之处。
  如果文件很大,像sort或compress一样,diff可能运行很长时间。假定这里有两个大文件warandpeace.txt和warandpeace.txt.old,命令diff warandpeace.txt warandpeace.txt.old会展示作者已经在整个文件中将名字“Ivan”改成“Aleksandr”。如果有几百个不同之处,其输出量是很大的。
  如果键入diff warandpeace.txt warandpeace.txt.old &,则系统会产生大量的输出,并很难停止-即使使用第七章给出的技术也不行。然而,如果键入:
  $ diff warandpeace.txt warandpeace.txt.old > txtdiff &
  则不同将被保存在文件txtdiff中,你可以以后再去查看。
**后台作业和优先级
  后台作业可以使你节省大量的闲散时间,但要记住这样的作业会占用许多的系统资源,如内存和处理器(CPU)。因为一次运行多个作业并不意味着比分别运行它们要快,实际上,性能通常有点差。
  系统上的每个作业都被设置了一个优先级,该数字告诉操作系统当该作业申请资源时应该给它多大的优先级(数值越高,优先级越低)。从shell输入的命令,无论是前台还是后台作业,通常都有一样的优先级,系统管理员可以以比用户更高的优先级运行它们。
  注意,如果你位于一个多用户系统,运行许多后台作业可能会将共享的系统资源耗尽,这时就需要考虑尽可能快的运行作业是否比成为一个好的用户更重要。
  要成为一个好的用户,可用一个UNIX命令降低任何作业的优先级:有的称为nice。如果键入nice command,这里command可以是带有管道、重定向等的复杂的shell命令行,该命令就会降低优先级运行。通过向nice给出数值参数可以控制降低的大小。细节请查看nice帮助页。
**特殊字符和引用
  <,>,|和&是对shell具有特殊含义的特殊字符。本章前面介绍的通配符(*,?和[...])也是特殊字符。
  表1-6列出了shell命令行内所有特殊字符的含义。其他一些字符在特定位置有特殊含义,如第三章和第四章介绍的正则表达式和字符串处理操作。
  
  表1-6 特殊字符
  字符    含义                  介绍的章
  ~       主目录                一
  `       命令替换              四
  #       注释                  四
  $       变量表达式            三
  &       后台作业              一
  *       字符串通配符          一
  (       启动子shell           八
  )       停止子shell           八
  \       引用下一个字符        一
  |       管道                  一
  [       开始字符集通配符号    一
  ]       结束字符集通配符号    一
  {       开始命令块            七
  }       结束命令块            七
  ;       shell命令分隔符       三
  '       强引用                一
  <">     弱引用                一
  <       输入重定向            一
  >       输出重定向            一
  /       路径名目录分隔符      一
  ?       单个任意子符          一
  !       管道行逻辑NOT         五
  
**引用
  有时需要照字面意思使用特殊字符,而不是其特殊含义,这称为引用。如果用单引号将字符串括起来,则引号内任意特殊字符的特殊含义均被屏蔽。
  可能需要引用一个字符串的常见之处是echo命令,它接受参数,并将其打印到标准输出。这样做的意义请参见第二章。shell对命令行只做很少的处理——大部分包含的特殊字符都在表1-6中列出。echo是使处理结果在标准输出上可利用的一种方式。
  当想要打印2 * 3 > 5 is a valid inequality时,假定键入:
  $ echo 2 * 3 > 5 is a valid inequality.
  则返回shell提示符,其他什么都没有,但生成了一个新文件,名字为5,它包含“2”,当前目录下所有文件的名称,以及字符串3 is a valid inequality.你要明白产生此结果的原因。
  然而,如果键入:
  $ echo '2 * 3 > 5 is a valid inequality.'
  结果就是给出的字符串的字面含义。不必引用整个字符串,只需要把包含特殊字符的部分引起来(如果不能确信,则应括起所有你认为可能是特殊字符的部分):
  $ echo '2 * 3 > 5' is a valid inequality.
  这样就会产生精确结果。
  注意,表1-6中将双引号(")列为弱引用符。双引号内的字符串与shell处理命令行的某些步骤有关(换句话说,它只将某些特殊字符特殊对待)。在后面章节将介绍应使用双引号的情况,第七章给出了shell对引用和其他命令行处理规则的详细解释。现在,你应该坚持使用单引号。
**反斜线转义
  另一种改变字符含义的方式是在其前面加上反斜线符号(\)。这称为反斜线转义字符。大多数情况下,对一个字符使用反斜线转义时,即引用了它。例如,
  $ echo 2 \* 3 \> 5 is a valid inequality.
  产生结果与使用单引号包围字符是一样的。要使用反斜线的字面含义,将其用单引号括起来('\')或对它使用反斜线转义(\\)。
  下面列出了引用特殊字符的更为实际的例子。一些UNIX命令参数常包含通配字符,需要将其转义以便shell不首先对其处理。最常见的命令是find,它在整个目录树中搜索文件。
  要使用find,应给出要搜索的树的根和描述要查找文件的特性的参数。例如,命令find . -name string搜索根为当前目录的目录树下名字匹配该字符串的文件(另外还有允许你根据文件大小、所有者、权限、最后访问日期等进行搜索的参数)。
  可以在字符串中使用通配符,但必须将其用引号括起来。这样find命令本身才能将其与要搜索的每个目录下的文件名匹配。命令find . -name '*.c'匹配当前目录及其子目录下文件名以.c结尾的所有文件。
**引用引号标记
  还可以使用反斜线符号在一个被引用的字符串内包含双引号。例如,
  $ echo \"2 \* 3 \> 5\" is a valid inequality.
  产生输出:
  "2 * 3 > 5" is a valid inequality.
  然而,这对引号表达式内的单引号不适用。例如,使用echo 'Hatter\'s tea party'不会得到Hatter's tea party的结果。可以用其他方式得到该限制效果。首先,试着消除引号:
  $ echo Hatter\'s tea party
  如果没有其他特殊字符(这里的情况),则工作正常。否则,使用下面的命令:
  $ echo 'Hatter'\''s tea party'
  这里,'\''(即单引号、反斜线符号、单引号、单引号)就是一个引用字符串内的单引号。为什么呢?这里的第一个'结束以('Hatter)开始的引用字符串,\'插入一个字面含义的单引号,下一个'开始另一个以单词“party”结束的引用字符串。如果理解了其中含义,就可以轻松解析由shell含义模糊的语法产生的其他令人混乱的问题了。
**续行
  一个有关的问题是在终端或工作站窗口上对超出一行的命令如何继续其文本。在理论上有一种很简单的方法:引用RETURN键。毕竟所有的RETURN实际上也是一个字符。
  实现方式有两种:通过用一个反斜线符号结束一行或通过一个未结束的引号标记(即将RETURN包含在一个被引用的字符串中)。如果使用反斜线符号,则在其到行结尾之间必须无内容——甚至是空格或TAB键。
  无论使用反斜线符号还是单引号,都是告诉shell忽视RETURN字符的特殊含义。键入RETURN后,shell理解你没有结束该命令行(即没有键入一个“真正的”RETURN),这样它就会以一个附属提示符响应,默认为>,并等待你结束该行。可以继续该行任意次。
  例如,如果要shell打印Lewis Carroll的《Alice's Adventures in Wonderland》(《爱丽丝梦游仙境》)中第五章的第一个句子,可以键入:
  $ echo The Caterpillar and Alice looked at each other for some \
  > time in silence: at last Caterpillar took the hookah out of its \
  > mouth, and addressed her in a languid, sleepy voice.
  或者键入:
  $ echo 'The Caterpillar and Alice looked at each other for some
  > time in silence: at last Caterpillar took the hookah out of its
  > mouth, and addressed her in a languid, sleepy voice.'
**控制键
  控制键——也就是按下CONTROL(或CTRL)再按另一键所表示的键——是另一类型的特殊字符,正常情况它不会出现在屏幕上或打印出来。例如常用的RETURN等同于CTRL-M(试试),你可能还用过BACKSPACE或DEL键来消除命令行上的键入字符。
  实际上,许多控制键包含了一些和你无关的功能;但是为进一步领会还是要知道它,而且,可能你会偶然键入它。
  对于控制键而言,最大的问题也许就是它们在系统间的差异性。表1-7列出了主要流行UNIX版本所支持的控制键。注意,DEL和CTRL-?是同一个字符。
  
  表1-7    控制键
  控制键        stty名称   功能说明
  CTRL-C        intr       中止当前命令
  CTRL-D        eof        输入结束
  CTRL-\        quit       如果CTRL-C无效,则中止当前命令
  CTRL-S        stop       停止输出到屏幕
  CTRL-Q                   重新开始到屏幕的输出
  DEL或CTRL-?   erase      删除最后的字符
  CTRL-U        kill       删除整个命令行
  CTRL-Z        susp       暂停当前命令(见第八章)
  
  可以使用stty命令找出当前的设置并在必要时改变它们,细节请参见第八章。如果系统上的UNIX版本来源于BSD(如SunOS和Ultrix),可以键入stty all查看控制键的设置,可以看到类似下面的内容:
  erase  kill   werase rprnt  flush  lnext  susp   intr   quit   stop   eof
  ^?     ^U     ^W     ^R     ^O     ^V     ^Z/^Y  ^C     ^\     ^S/^Q  ^D
  ^X标记表示CTRL-X。如果你的UNIX版本来自系统III或系统V(包括AIX、HP/UX、SCO、Linux和Xenix),键入stty -a。输出结果会包含下面的信息:
  intr = ^c; quit = ^|; erase = DEL; kill = ^u; eof = ^d; eol = ^`;
  swtch = ^`; susp = ^z; dsusp <undef>;
  最经常使用的控制键可能是CTRL-C,有时称为中断键。该键结束——或试图结束当前运行的命令。当你输入一个命令后,发现它运行时间过长、给出的参数不对或你改变主意不想再运行它时,可以使用此键。
  有时,CTRL-C会失效;这种情况下,如果想停止一个作业,试试CTRL-\。但最好先试试CTRL-C,然后再试CTRL-\,第八章解释了其原因。现在,可以说CTRL-C给出了在退出前清除一个运行作业的机会。这样文件和其他资源就不会处于不响应的状态。
  上面介绍过CTRL-D的例子。当运行一个从键盘接受标准输入的命令时,CTRL-D告诉处理器输入结束——就好像处理器正在读取一个文件并到达了文件末尾。mail实用程序中经常发生这种情况。当你键入消息时,可通过键入CTRL-D结束。这将告诉mail消息已完成,可以准备发送了。大多数接受标准输入的实用程序都将CTRL-D理解成输入结束字符,但有些这样的程序也接受类似q、quit、exit等的命令。
  CTRL-S和CTRL-Q称为流控制字符。它们是停止和重启从一个设备到另一个设备的输出流的一种过时方式(例如,从计算机到终端),当输出流速度很慢时它们就派上用场了。在当今高速局域网和拨号网络的时代,它们几乎已经无用。实际上,在后一种情况下,CTRL-S和CTRL-Q基本上成了麻烦。关于它们唯一需要知道的是如果屏幕输出不动了,那可能是你偶然键入了CTRL-S。键入CTRL-Q就可以重启输出,期间键入的任意键也会生效。
  最后一组控制键给出编辑命令行的方式。DEL为回退符(backspace)(实际上,系统使用真正的BACKSPACE或CTRL-H键充做“erase”而不是DEL)。用CTRL-U删除整个行使你可以重新开始编辑。它们也已经被取代。下一章将介绍bash的编辑模式,包括一些最有用的特性。比这里给出的受限制的编辑功能要强大得多。
**帮助
  在bash中其他shell所没有的一个特性是在线帮助系统。使用help命令可以得到关于bash中其他命令的信息。如果键入help,就会得到一个内置的shell命令的列表,同时伴有其选项设置。
  如果给出help加shell命令名,将会得到该命令的详细描述:
  $ help cd
  cd: cd [-PL] [dir]
    Change the current directory to DIR.  The variable $HOME is the
    default DIR.  The variable $CDPATH defines the search path for
    the directory containing DIR.  Alternative directory names in
    CDPATH are separated by a colon (:).  A null directory name is
    the same as the current directory, i.e. `.'.  If DIR begins with
    a slash (/), then $CDPATH is not used.  If the directory is not
    found, and the shell option `cdable_vars' is set, then try the
    word as a variable name.  If that variable has a value, then cd
    to the value of that variable.  The -P option says to use the
    physical directory structure instead of following symbolic links;
    the -L option forces symbolic links to be followed.
  还可以给出help加部分名字,这种情况下将会给出匹配该名称的所有命令的详细信息。例如,键入help re会得到read、readonly和return的信息。部分名字可以包含通配符,这时需要引用该名字以确保通配符不被解释成文件名。因此help re等价于help 're*'。而键入help 're??'将只返回read的信息。
  有时help显示的信息会多于一屏,并将向下滚动。可以使用more命令每次显示一屏,只需键入help 命令 | more。
 

猜你喜欢

转载自blog.csdn.net/chenzhengfeng/article/details/81558700