3.1 shell基本功能与基本概念
Linux系统的shell作为操作系统的外壳,为用户提供使用操作系统的接口。在UNIX/Linux中有多种shell,其中默认使用的是bsh或bash,简称sh。Fedora系统默认的shell为bash(bourne again shell),它包含了bsh的所有特性。
3.1.1 shell基本功能
shell是用户和Linux内核之间的接口程序,作为人机交互的桥梁,其功能有6个:命令解释执行、文件名替换、I/O重定向、通信管道建立、系统环境设置和shell编程。
首先,shell是一个命令解释器,负责人机交互,接收并解释用户输入的命令,并将它传送到系统内核,由内核执行完毕后,再将结果按指定格式传递给用户或显示到终端屏幕上。
shell拥有自己内部命令集,也能被系统中其他应用程序调用,如改变工作目录命令cd,是内部的;还有一些命令,如文件复制命令cp和文件移动命令mv,是存在于文件系统中某个目录下的程序;也还有一些命令,如echo,既是内部的也是外部的。对用户而言,一般不必关心一个命令是内部的还是外部的。默认情况下,当用户从键盘输入一个命令时,shell首先检查它是否带有路径,若有则按路径搜索它,找到后执行,找不到则报错。若没有带路径,则首先检查它是否为内部命令,若是则执行;若不是,再检查是否是一个外部命令或应用程序。此时,shell在PATH指定的搜索路径里寻找。如果能够成功找到命令,则shell在进行相应的处理后将其传给内核执行,执行完毕后,再将输出返回给用户。如果找不到该可执行文件,将会显示一条错误信息。对于既是内部又是外部的命令,或者说内、外部命令同名的,shell也有一种机制可以有选择的执行,但默认情况下是执行内部命令。
shell的另一个重要特性是它自身就是一个解释型的程序设计语言。shell程序设计语言支持绝大多数在高级语言中能见到的程序元素,如函数、变量、数组和控制结构等。任何在提示符中能输入的命令,都能放到一个可执行的shell脚本程序中,作为一个语句来使用。shell编程将在第10章专门介绍,shell的其他功能将在稍后陆续介绍。
3.1.2 字符与保留字
1.字符
shell中除使用普通键盘可以输入的字符外,还有一些具有特殊含义和功能的特殊字符。使用它们时,应注意其特殊的含义和作用范围。
1)白空格
在UNIX/Linux系统中空格和Tab称为白空格(White Space),主要用于命令行中命令名、参数及选项的分隔。在特殊时候,白空格中也包含回车字符。
2)通配符
通配符用于模式匹配,如文件名匹配、路径名搜索、字符串查找等。常用的通配符有“*”、“?”和括在方括号“[]”中的字符序列。用户可以在作为命令参数的文件名中包含这些通配符,构成一个所谓的“模式串”,在执行过程中进行模式匹配。
(1)*:代表从它所在位置开始的任何字符串,如“f*”匹配以f打头的任意字符串。但应注意,文件名前的圆点“.”和路径名中的斜线“/”必须显式匹配。例如,“*”不能匹配.file,而“.*”可以匹配.file。
(2)?:代表它所在位置上的任何单个字符。
(3)[ ]:代表一个指定范围的字符,只要文件名中“[]”位置处的字符在“[]”中指定的范围之内,那么这个文件名在此处可以被匹配。方括号中的字符范围可以由直接给出的字符组成,也可以由表示限定范围的起始字符、终止字符及中间的连字符“-”组成。例如,f[a-d]与f[abcd]的作用相同。[ ]内的第一个字符若“^”或“!”,则表示非运算,意为不匹配[ ]内的任何字符,如f[!a-d]或f[^a-d]表示不匹配方括号内的a-d的字符集,但匹配[ ]之外的字符集。
表3-1给出了通配符的具体含义及实例。
表3-1 通配符含义举例
需要说明的是,连字符“-”仅在方括号内且在中间时有效,表示字符范围,如在方括号外面或在方括号内最前或最后就成为普通字符了;字符“^”和“!”只有在方括号内且位于开始位置才起“非”的作用;而“*”和“?”只在方括号外面是通配符,若出现在方括号之内,它们也失去通配符的能力,成为普通字符。例如,模式“-a[*?]abc”中只有一对方括号是通配符,“*”和“?”均为普通字符,因此它匹配的字符串只能是“-a*abc”或“-a?abc”。
以上是通配符在文件名模式匹配中的应用,但它们在shell编程或不作为文件名模式匹配时还具有另外的意义(参见正则表达式)。总的来说,它们对于shell具有比较特殊的意义,因此在正常的文件名中不应出现这些字符。
3)注释符与注释
在所有编程语言中都有注释,以增加程序的可读性。在shell命令或shell编程中,规定从字符“#”开始以后到行末的部分作为注释,因此以“#”开头的行是注释行。以后将会发现,事情并非总是这样,例如,$#表示命令行参数的个数,而${#name}表示变量name的值作为字符串的长度。若在行中需要使用“#”开始注释,则“#”前至少要有一个白空格。
4)转义字符
UNIX/Linux系统中还有一个特殊字符“\”,用于对某些特殊字符的表示,部分特殊字符如表3-2所示。基于此,字符“\”被称为转义字符(ESCape char)。
表3-2 Linux的部分特殊字符
2.特殊键
在UNIX/Linux系统中,键盘上常规键的用法与其他操作系统相同。但对于特殊键和组合键其意义就可能不同于其他的操作系统。另外,在UNIX/Linux系统中的意义和使用方法可能还与所使用的终端类型有关。UNIX/Linux系统的部分特殊键如表3-3所示。
表3-3 UNIX/Linux系统的部分特殊键
3.保留字
任何编程语言或系统都有一定数量的保留字,bash的部分保留字如下:
!、[,]、(,)、{,}、break、continue、cd、echo、eval、exec、exit、export、function、getopts、hash、pwd、read、readonly、return、select、set、shift、test、time、trap、type、ulimit、unset、umask、wait、for、do、done、case、in、esac、if、else、elif、fi、while、until。
3.1.3 文件命名及文件类型
1.文件与文件名
多数操作系统都有文件的概念。文件是被命名(称为文件名)的存储在某种介质(如磁盘、光盘和磁带等)上的一组信息的集合。UNIX/Linux的文件均为无结构的字符流形式。文件名是文件的一种标志,一般情况下,它由字母、数字、下画线和圆点组成的字符串构成。其实,在UNIX/Linux系统中,可用于文件名的字符是广泛的,只要是用户能够输入的字符均可用于文件名,但在实际应用中应该定义有实际意义的文件名。
Linux支持长文件名,但要求文件名的长度限制在255个字符以内。为了便于管理和识别,用户可以把扩展名作为文件名的一部分。圆点“.”用于区分文件名和扩展名。扩展名对于文件分类是十分有用的。用户可能对某些大众已接纳的标准扩展名比较熟悉,如C语言程序以.c为扩展名。用户也可以根据自己的需要,定义自己的文件扩展名。
2.文件类型
UNIX/Linux系统中有三种基本的文件类型:普通文件、目录文件和设备文件。
1)普通文件
普通文件是用于存放数据的文件,它是用户最经常面对的文件,它又分为文本文件和二进制文件。
(1)文本文件:以可阅读的ASCII码形式存储在计算机存储器中,大多数情况下,是以“行”为基本结构的一种信息组织和存储方式。
(2)二进制文件:是用户一般不能直接读懂的文件,只有通过相应的软件才能对其进行操作。二进制文件一般是经编译程序编译后生成的可执行程序、图形、图像或声音等。
2)目录文件
目录文件用于存储一组相关的文件项信息或文件说明信息,其中包括文件名及其属性的信息。在UNIX/Linux系统中,它只包括文件名和i节点号等相关信息,而文件的属性信息保存在i节点号信息中。目录文件在形式上同普通文件一样,但具有目录属性,只能用目录管理命令来访问和管理。
目录文件中的文件项信息结构如下:
struct dir_entry { __u32 inode; //i节点号,32位无符号长整数 … … … … char name[255]; //文件名,最长为255 };
当文件系统对文件进行搜索时,首先按名存取,查到文件名,再由文件名得到对应的i节点号,最后通过i节点号找到文件属性和位置信息,从而实现对文件的访问。
一个目录文件内至少要有两个文件项:“.-”当前目录;“..-”上级目录。
目录文件的大小只能增加,尽管可以从目录中删除文件或子目录,但不能使目录变小。使目录变小的方法是删除后重建。
目录文件往往简称为目录或子目录,在图形界面下称为文件夹。因为目录也是文件,所以它的命名规则和文件命名规则相同。
3)设备文件
设备文件是UNIX/Linux系统的一个重要特色。UNIX/Linux系统把每一个I/O设备都看成一个文件,与普通文件一样处理,这样可以使文件与设备的操作尽可能统一。从用户的角度来看,对I/O设备的使用和一般文件的使用一样,不必了解I/O设备的细节。
设备文件与普通文件和目录文件不同,它除了在目录文件和文件说明信息表(i节点)中占据相应的位置之外,并不占有实际的物理存储空间。当用户使用设备文件时,可通过设备的名称得到其i节点,然后通过其中的主、次设备号取得与内核中的设备驱动程序的联系,从而达到访问设备的目的。
为了区分普通文件、目录文件和不同设备文件之间的不同,系统把设备文件称为“设备特别文件”,简称设备文件。通常情况下,设备文件存放在系统的设备目录/dev下。常见的设备文件有以下几种。
(1)块设备文件(b):以块方式存取的设备,如硬盘、磁盘、磁带等。
(2)字符设备文件(c):以字符方式存取的设备,如字符打印机、显示器等。
(3)管道设备文件(p):用于进程之间通信的先进先出(FIFO)“临时文件”。
(4)符号链接文件(l):用于通过此文件的内容指向它所链接的文件或资源。
(5)套接字文件(s):套接字分为文件套接字和网络套接字。文件套接字存在于文件系统中,让用户以文件的方式访问网络连接。
管道设备有以下两类。
(1)无名管道:主要用于有直接继承关系的父子进程之间的通信。当创建无名管道的进程结束后,无名管道自动消失。
(2)命名管道:命名管道是一种特殊的设备文件,在文件系统里以文件形式存在。由于是以文件形式存在的,所以使有家族关系的进程间可使用它进行通信,不具有家族关系的进程也使用它进行通信。在通信结束后,若不主动删除它,它不会自动消失,仍以文件的形式存在于文件系统中。
文件链接有以下两类。
(1)硬链接(-):两个或多个文件名共用一个文件体,也就是说一个文件可以具有多个不同的名字,但它们具有相同的i节点号。
(2)符号链接(l):与硬链接不同的是,它们不是同一个文件,各有自己的i节点,但可以通过链接内容访问被链接文件。符号链接也称为软链接,在Windows系统中被称为快捷方式。
硬链接来自UNIX系统本身,不能实现跨越文件系统的链接。符号链接是在现代计算机应用基础上发展起来的,具有广泛的灵活性,可以跨文件系统链接,可以指向网络资源,甚至可以指向一个不存在的资源或位置。
3.1.4 目录结构与路径
1.目录与目录结构
在操作系统中有大量的文件,如何有效地组织与管理它们,并为用户提供一个使用方便的接口是文件系统的一大任务。UNIX/Linux系统以文件目录的方式来组织和管理系统中的所有文件。所谓文件目录就是将所有文件项或文件的说明信息采用树形结构组织起来——即常说的树形目录。UNIX/Linux系统中,整个文件系统只有一个“根”(root),然后在根上分“杈”(directory),任何一个分杈上都可以再分杈,也可以长出“叶子”。“根”和“杈”在Linux中被称为“目录”或“文件夹”,而“叶子”则是一个个的文件。在UNIX/Linux系统中,文件系统的根目录用符号“/”表示。
UNIX/Linux系统通过目录将系统中所有的文件分级、分层组织在一起,形成了树形层次结构,它以根目录为起点,所有其他的目录都由根目录派生而来。
实际上,各个目录节点“之下”都会有一些文件或子目录,并且系统在建立每一个目录时,都会自动为它创建两个目录文件,一个是“.”,代表该目录自己;另一个是“..”,代表该目录的父目录,通过它取得与上级目录的联系。对于根目录,“.”和“..”都代表其自己。
UNIX/Linux提供管理文件和目录的方便途径。用户可以方便地从一个目录切换到另一个目录,也可为自己创建文件或目录,或可以把一个目录下的内容移动或复制到另一个目录下,并且还可与系统中的其他用户共享目录或文件。用户还可以设置目录和文件的管理权限,以便进行访问控制。
Linux是一个多用户系统,操作系统本身的程序或数据存放在以根目录开始的某些专用目录中,有时被指定为系统目录。用户也可创建自己的目录,用于存放自己的程序或数据。Linux系统的目录结构如图3-1所示(需要说明是,不同版本或厂家的系统中,目录结构可能存在差异)。
图3-1 Linux系统的目录结构
/:系统的根目录。
/dev:系统的设备目录,其中存放着几乎所有的设备文件。
/etc:存放Linux系统和大部分应用软件的配置文件。
/home:用户家目录所在的目录。家目录也称为主目录。默认情况下,每创建一个用户,就会在这里新建一个与用户同名的目录,给该用户一个自己的空间,如/home/lisi为用户lisi的家目录。
/root:root用户的家目录。
/lost+found:丢失文件的存放目录。当系统因非法关机等原因造成文件系统损坏时,经修复后一些丢失的文件存放在这里。
/mnt:外部设备的挂装点,用于挂载设备文件。在系统安装后,可用于安装外来文件系统。用户也可以在此目录下创建自己的目录,如cdrom、usb和hd等用于安装光盘、U盘、硬盘分区或其他设备。
/boot:Linux的启动目录。系统内核文件、引导器GRUB等存放在这里。
/proc:伪文件系统目录,参见5.3.1节。
/usr:用户级目录。
/tmp、/usr/tmp:临时目录。
/sbin、/usr/sbin:系统级的命令与工具目录。
/bin、/usr/bin:用户级的命令与工具目录。
/usr/include:C语言,内核编译所需要的头文件存放目录。
/lib、/usr/lib:库文件存放目录,其中有表态库和动态库。
/usr/src:Linux源代码目录,编译内核时使用。
/var:通常用来存放一些经常变化的内容,如各种网络服务的工作目录都在这里。
/var/log:系统日志目录。
/var/spool:存放邮件、新闻、打印队列任务等。
/usr/share:存放共享使用的,如各种共享或在线帮助等。
2.工作目录、用户主目录与路径
1)工作目录与用户主目录
自从用户登录到系统中之后,每时每刻都“处在”某个目录之中,此目录被称为工作目录或当前目录(Working Directory)。工作目录用“.”表示,并且可以随时改变。用户初始登录到系统中时,其主目录或家目录(Home Directory)就成为其工作目录。每个用户都有自己的主目录,不同用户的家目录一般互不相同。
默认情况下,用户刚登录到系统中时的工作目录便是该用户家目录。root用户的主目录为/root,其他用户的家目录是在/home下的与登录名相同的目录。用户可以通过一个“~”字符或环境变量HOME来引用自己的家目录。
工作目录或当前目录可以根据需要随时改变,但用户家目录不可轻易改变。
2)路径
路径是指从树形目录中的某个目录层次到某个文件或目录路线。此路径的主要构成是所经过的目录名称,中间用“/”分隔。
任一文件在文件系统中的位置可以由相对路径或绝对路径来决定。绝对路径是指从“根”开始的路径;相对路径是从用户工作目录开始的路径。应该注意到,引入链接的文件系统中,从某位置到另一位置的路径可以不止一个,因此又有物理路径和逻辑路径的概念。绝对路径是确定不变的,而相对路径则随着用户工作目录的变化而不断变化。
用户要访问一个文件时,可以通过路径名来实现,并且可以根据要访问的文件与用户工作目录的相对位置来引用它,而不需要列出这个文件的完整路径名。例如,处在图3-1中dir目录的用户要访问目录usrn中的文件file1,可通过相对路径../../usrn/或绝对路径/home/usrn/来实现,此时带有路径的文件名可表示为../../usrn/file1或/home/usrn/file1。也可把这种带有路径的文件名称为文件名。
3.1.5 shell命令解释及执行
用户登录到Linux系统时,可以看到一个shell提示符,说明用户可以在其后输入命令。用户可以在提示符后面输入任何命令及参数。例如:
#date
用户登录成功后,实际上进入了shell,它遵循一定的语法将输入的命令加以解释并传给系统。命令行中输入的第一个字必须是一个命令的名字,若有,则以后的内容为由分隔符分隔的选项或参数,格式如下:
命令名 [选项 …][参数 …]
1.分隔符
在Linux系统中默认的分隔符为白空格。
2.选项和参数
参数是Linux命令操作的对象,而选项则影响命令对对象的操作行为。选项是由符号“-”引导的字符或字符串,“-”是必需的,Linux用它来区别选项和参数。在Linux系统中,选项有两种形式:一种是传统UNIX风格的选项,以“-”开始,后面紧跟一个字符;另一种是GNU风格的选项,以“--”开始,后面紧跟着完整的英文单词或由“-”连接的单词组合,用来说明选项的意义(参见示例)。考虑到书写等因素,本书更多的是介绍传统UNIX风格的选项格式,只有在必要时才采用GNU风格的选项。两种风格的选项使用示例如下:
#date #以默认方式显示当前日期和时间 # #以Greenwich Mean Time显示当前日期和时间 #date-u #传统UNIX风格选项 #date--utc #GNU风格选项 #date--universal #GNU风格选项 # #显示文件/etc/passwd的属性信息 #ll/etc/passwd #默认模式或-u。传统UNIX风格选项 #ll-n/etc/passwd #传统UNIX风格选项 #ll--numeric-uid-gid/etc/passwd #GNU风格选项 #ll-n--time-style=iso/etc/passwd #传统UNIX和GNU风格选项混合使用
由上可见,选项-u影响了date的输出,-n等选项也影响了ll的输出。由于Linux命令一般都带有多个参数或选项,描述不便,所以本书在以后的描述中,在不引起混淆的情况下不再区分参数和选项,统称为参数。
3.命令行编辑特性
1)命令和文件名扩展
bash命令行具有命令和文件名扩展特性。当输入一个还没完成的命令或文件名时,只需按“Tab”键就能激活命令和文件名扩展特性,从而完成该命令的剩余输入。如果有多个命令或文件的前缀相同,bash将响铃并等待用户输入足够的字符,以便选择唯一的命令或文件名。如果找到,系统将自动补齐搜索到的命令或文件名,用户按“Enter”键后,系统将执行这条指令。例如,若此时系统中只有一个以“pre”开头的文件preface,当用户输入cat pre然后再按“Tab”键,将补全整个内容。
#cat pre #按“Tab”键后变为 # cat preface
当备选文件名不唯一时,按下“Tab”键后,bash会在补齐最大可能长度后响铃,若此时用户先后按下“Esc”键和“?”键,bash将列出所有与输入的字符串相匹配的文件名供etyn选择,然后用户可再输入少量可以区分文件名的字符,再按“Tab”键补全命令行。如果需要补全的文件名很长且区分度不高,则可重复使用以上方法,直到最终补全命令行。
设当前目录内有dcrease.c、deveice.txt、document和docudrama文件,要显示document文件的内容,可按以下步骤进行。
#cat do<Tab> #按“Tab”键后,响铃,结果如下: #cat docu<Tab> #按2次“Tab”键后,或先后按“Esc”键和“?”键,显示如下可选内容: docudrama document #cat docum #输入m,再按“Tab”键补齐 #cat document #按“Enter”键执行命令
对于命令本身也可按同样方法扩展。
从上面可以看到,bash总是尽力根据用户输入的信息来补全命令。当无法根据现有信息补全命令时,提示用户给出进一步的信息,然后根据用户的提示来补全命令。
2)命令行编辑
bash允许用户对正在输入的命令行进行编辑,编辑过程主要通过如表3-4 所示的编辑键来完成。
表3-4 bash的命令行编辑键
3)历史记录
命令行实际上是可以编辑的一个文本缓冲区,在按“Enter”键之前,可以对输入的文本进行编辑。在Linux的bash中,回车后命令行也被保留在历史记录文件中,可通过光标键“↑”和“↓”上下翻阅历史记录,对出现的当前记录可以进行编辑和使用。利用这一功能可以重复执行以前执行过的命令,而无须重新输入该命令。
在Linux系统中,为每个用户在~/.bash_history文件内预设了1000条记录的历史,使用命令:
history
会列出所有历史记录,并为每条记录一个编号。若用户要重新执行某条历史记录的命令,可以使用以下格式:
!记录编号
需要说明的是,就某一个历史命令来讲,“记录编号”是变化的,按以上格式引用的“记录编号”必须是上次刚刚执行过的history命令显示的编号,否则可能会执行错误的命令。
用户还可使用格式:
history n
让其显示最近的n条历史记录。
history命令的使用示例如下:
#history #显示所有历史记录 #history 5 #显示最近5条历史记录 999 ls-l/ … … … … … … #!999 #执行第999条历史记录:ls-l/ ls -l /
3.1.6 环境变量与变量
1.环境变量
shell在开始执行时就已经定义了一些和系统的工作环境有关的变量,用来控制用户程序的执行,或为用户程序的执行提供环境支持,用户还可以重新定义这些变量。环境变量可用命令env或set来查询。常用的shell环境变量如下所述。
(1)HOME:家目录。默认情况下,用户成功登录后所在目录的完全路径名。
(2)LOGNAME:登录用户名。用户成功登录时使用的用户名。
(3)IFS:命令行内部参数、选项间的分割符。默认为白空格。
(4)PATH:由冒号分隔的目录路径名。shell将按PATH变量中给出的路径顺序搜索键盘输入的命令名,找到的第一个与命令名称一致的可执行文件将被执行。
(5)TERM:终端的类型。常用的有Linux和xterm等。
(6)PWD:当前工作目录的绝对路径名。该变量的取值随cd命令而变化。
(7)OLDPWD:旧目录,刚刚离开的目录。该变量的取值随cd命令而变化。
(8)PS1:主提示符。默认情况下,超级用户的主提示符是#,普通用户的主提示符是$。
(9)PS2:辅提示符(继续使用提示符)。在shell接收用户输入命令的过程中,如果用户在输入行的末尾输入“\”然后按“Enter”键,或者当用户按“Enter”键时shell判断出用户输入的命令没有结束时(如引号或括号不成对等),就显示这个辅助提示符,提示用户继续输入命令的其余部分,默认的辅助提示符是“>”。
2.变量
在UNIX/Linux系统中,除了环境变量外,用户还可以定义自己的变量。定义后的变量就像在其他程序设计语言里一样被引用。引用变量时需要使用“$”作为变量名的前导字符。
变量的定义方法:
var_name=var_value
变量的定义和使用示例:
#x=123 #定义一个数值变量x,其值为123 #y='I am a student' #定义一个字符串变量y,其值为'I am a student' #echo$x$y$HOME #显示用户变量x、y和环境变量HOME的值 #z="$x$y" #由变量x和y定义变量z,其值为'123 I am a student'
由于shell是解释语言,所以在变量定义和使用时不需要声明类型,shell在使用变量时会根据变量的值来具体确定变量的类型。
3.1.7 标准流与输入/输出重定向
1.标准流
当执行一个命令时,shell通常会自动为其打开三个标准流(文件):标准输入流、标准输出流和标准错误流。三种标准流所使用的文件描述符及物理设备如表3-5所示。默认情况下,程序将从标准输入流读取输入数据,而将正常输出数据输出到标准输出流,将错误信息送到标准错误流。
表3-5 标准I/O流及相关设备信息
直接使用标准流存在以下问题:
(1)输入数据从终端输入时,用户输入的数据只能用一次,易出错且修改不便。
(2)输出到终端屏幕上的信息只能看,不便使用。
UNIX/Linux系统的标准I/O重定向可以解决这些问题。
2.I/O重定向
I/O重定向(也称为改道)是指通过文件的形式实现标准I/O流。I/O重定向可以通过以下符号实现。
(1)<:标准输入重定向。
(2)>:覆盖方式标准输出重定向。若文件不存在,则创建;否则覆盖。
(3)>>:追加方式标准输出重定向。若文件不存在,则创建;否则在其尾部追加。
(4)&>或>&:标准输出和标准错误同时重定向(不能用于追加方式)。
1)输入重定向
输入重定向是指让命令(或可执行程序)的标准输入从指定的文件中读取,即输入可以不来自键盘,而来自一个指定的文件。因此,输入重定向主要用于解决一个命令的输入源数据,尤其是需要大量输入数据的问题。
例如,命令wc可用于统计指定文件包含的行数、单词数和字符数。
#wc/etc/passwd #统计文件/etc/passwd的行、词和字符信息。也可使用: #wc</etc/passwd #标准输入重定向
2)输出重定向
输出重定向是指把命令(或可执行程序)的标准输出保存到文件中,可用两种方式:覆盖方式(>)和追加方式(>>)。例如:
#ls>/tmp/dir.out #覆盖方式,将当前目录信息重定向到文件/tmp/dir.out #ls/usr>>/tmp/dir.out #追加方式,将/usr信息重定向追加到/tmp/dir.out
输出的重定向是有副作用的,一是它可能覆盖已经存在的文件,二是它在一个具有完整意义的文件后面追加了一些外来或无意义信息。
3)标准错误重定向
标准错误重定向与标准输出重定向意义相同,但它是针对标准错误的。在对标准输入和标准输出重定向时没有使用文件号,但在对标准错误改道时要使用文件号2。方法是:
命令 2> 文件 或 命令 2>> 文件
在2和>之间不能有空格。例如:
#ls-l/home/w-w-w 2>/tmp/err.out #将标准错误重定向到文件/tmp/err.out #cat/home/w-w-w 2>>/tmp/err.out #将标准错误追加到文件/tmp/err.out #cat/tmp/err.out #查看文件/tmp/err.out内容
4)标准输出和标准错误同时重定向
使用符号&>或>&可以让一个程序的标准输出和标准错误以覆盖方式同时重定向到一个文件。需要注意的是,此方式不能用于追加,也就是说不能使用符号&>>或>>&。
3.1.8 管道
管道机制是在两个或多个进程之间建立一种连接,使前一个命令的输出作为后一个命令的输入。管道机制常用于进程间的通信。将一个程序或命令的输出作为另一个程序或命令的输入,有两种方法:
(1)通过临时文件将多个命令或程序结合在一起。
(2)通过UNIX/Linux所提供的管道功能。
后者优于前者。
1.管道
实现管道机制的符号是“|”,其方法为:
命令1| 命令2| … |命令n
例如:
#ls-l/dev|wc-l #统计系统设备目录/dev内有多少文件或子目录 #cat sample.txt|grep "High"|wc-l #统计sample.txt内有多少行包含High
2.三通
有时,对某文件进行处理时,既要在屏幕上看到输出,又要同时保存一个副本,这时可使用管道与tee命令配合来实现三通。
tee命令的功能是读取标准输入的数据,并将其内容输出到指定文件,其用法为:
tee [-ai] [files]
tee命令的选项如表3-6所示。
表3-6 tee命令的选项
tee命令的使用示例:
# #将当前目录的*.txt文件目录显示在屏幕上,同时保存两个副本f1和f2 # ls *.txt | tee f1 f2
3.1.9 引号机制、命令替换与参数替换
1.引号机制
在shell中有三种引号:单引号('),双引号(")和反单引号(`),前两者用于变量定义,后者用于命令替换。
1)单引号
由单引号括起来的字符都作为普通字符。特殊字符用单引号括起来以后,也会失去原有意义。例如:
#string='$PATH' #定义string变量,其值为'$PATH' #echo$string #显示变量的值:$PATH
2)反单引号
反单引号的作用是命令替换。所谓命令替换是指反单引号内的内容将作为命令首先被执行,然后再将命令执行的结果替换反单引号及其括住位置的信息。例如:
#x=`pwd` #通过命令pwd替换定义变量x。pwd的功能是显示用户工作目录 #y=`whoami` #通过命令whoami替换定义变量y。whoami的功能是显示用户名 #echo$x$y #显示变量x和y的内容
命令替换的另一种形式是:
$(cmd)
它的作用与`cmd`相同。例如:
#MyⅤar=$(whoami) #通过命令whoami替换定义变量MyⅤar #echo$MyⅤar$(pwd) #显示变量MyⅤar和$(pwd)的值
命令替换也称为命令扩展。
3)双引号
双引号的作用与单引号的功能基本一样,可用来定义变量,所不同的是,在双引号内可进行变量替换和命令替换。双引号中的特殊字符仍具有特殊意义。所谓变量替换,是指在双引号中对变量的引用将被替换成变量的值,变量替换也叫变量扩展。若在双引号内使用特殊字符且又把它作为特殊字符本身来看待,则必须使用转义字符,如双引号中的双引号必须表示为\",\必须表示为\\。
双引号使用示例:
#myname=`whoami` #通过命令替换定义变量myname # #定义变量myself,注意使用了变量替换和命令替换 # myself="I am a student, my uname is $myname and my work dir is `pwd`." # #定义变量myself1。注意使用了继续行和转义字符 # myself1="I am a student my uname is \"$myname\" \ and my work dir is \"`pwd`\"" #echo$myself #显示变量myself的值 #echo$myself1 #显示变量myself1的值
2.参数替换
在引号机制中已经看到变量替换和命令替换。参数替换主要是指命令行参数中的变量和命令替换。在执行命令时,其命令行参数既可以是常量也可以是变量,还可以包含命令替换。设有命令DispAllVar用于显示它自己的所有的命令行参数,设变量x的值为“test parm”,则命令
DispAllⅤar I am `whoami` the value of x is $x
的输出为:
I am root the value of x is test parm
由此可见,在程序的执行过程中`whoami`被替换为root,$x被替换为“test parm”。像这样,参数在命令行被替换的过程称为参数替换。
3.1.10 shell命令的执行
1.标准流与程序I/O
当一个shell命令执行时,系统首先为其打开三个标准流:标准输入、标准输出和标准错误。程序执行时,默认从标准输入读、向标准输出写、出错时将错误信息写向标准错误。
当一个程序需要从输入文件中读取、向输出文件输出信息时,若没有为之提供相关文件,则使用标准I/O:将输入文件定向到标准输入,将输出文件定向到标准输出。需要时,也可将标准输入和标准输出重定向通信管道。当从文件读取信息时,结束符为文件结束标志EOF;当从标准输入读取信息时,输入完成时的结束符为^D。例如,命令grep是用于信息过滤的,现在通过命令ls /dev > x产生一个文件x,然后用于grep从x中过滤软盘设备信息。方法是:
grep fd x #使用文件 grep fd<x #使用标准输入
如果仅为了统计ls /dev输出中的信息,完全可以不使用中间文件x,而直接使用管道:
ls/dev|grep fd #使用管道
如果需要,也可将以上命令的输出重定向到输出文件或通过管道传给其他程序,例如:
ls/dev|grep fd>y #通过标准输出重定向到文件y ls/dev|grep fd|wc #通过标准输出重定向到管道
由于UNIX/Linux系统的shell命令大都做成了从标准输入读、向标准输出写的过滤器,因此用户可将从标准输入读的信息重定向到标准输入,将向标准输出写的信息重定向到文件,也可将标准错误重定向到文件。
2.命令的返回值
UNIX/Linux系统默认约定shell命令在结束时向调用者返回一个状态码或返回值,以表示执行成功与否。一般情况下,返回值为0表示成功,非0表示失败。当命令是由管道连接的命令串时,最后执行的一个命令返回作为整个命令串的返回值。返回值可以通过$?来访问。
命令:
ls/dev|grep fd #执行命令 echo$? #显示返回状态(值)
将输出0,表示/dev目录内有软驱设备文件,而命令:
ls/dev|grep FD #执行命令 echo$? #显示返回状态
将返回非0(此处为1),表示ls /dev的输出中没有字符串FD。
3.1.11 shell种类
UNIX/Linux系统中的shell有多种类型,其中最常用的几种是Bourne shel(l sh或bsh)、Bourne again shell(bash),C shell(csh)、tc shell(tcsh)、Korn shell(ksh)和Z shell(zsh)等。
bsh是Stephen Bourne在贝尔实验室设计的shell,是UNIX shell的一种,并且在每种UNIX上都可以使用。在众多的UNIX系统中,bsh基本保持一致,它是大多数UNIX系统默认的shell。
bash是GNU工程中使用的shell,即GNU操作系统的默认shell。bash是在bsh基础上发展起来的。bash与bsh稍有不同,它兼有csh、tcsh和ksh的特色。大多数bsh脚本程序在其上不加修改就可运行,但bash的脚本程序则不一定能在bsh上运行。bash实现了IEEE POSIX 1003.2/ISO 9945.2 shell和工程规范。Linux操作系统默认的shell是bash。
csh是在bsh之后、ksh之前,由vi的设计者Bill Joy编写的,它不是bsh的扩展。它采用C语言作为语法模型,是一种比Bourne shell更适于编程的shell。Linux为喜欢使用C shell的人提供了tcsh。tcsh是C shell的一个扩展版本。tcsh包括命令行编辑、可编程单词补全、拼写校正、历史命令替换、作业控制和类似C语言的语法,它不仅和Bash shell提示符兼容,而且还提供比Bash shell更多的提示符参数。
ksh是由贝尔实验室的David G. Korn命名的,可以说它是对bsh的发展,在大部分内容上与bsh兼容,几乎所有bsh脚本程序都可以在ksh上运行,但反之不行。Korn shell集合了C shell和Bourne shell的优点。Linux系统提供了pdksh(ksh的扩展),它支持任务控制,可以在命令行上挂起、后台执行、唤醒或终止程序。
Linux也并没有冷落其他shell用户,还提供了一些流行的shell,如ash、zsh等。每个shell都有它的用途,有些shell是有专利的,有些能从Internet或其他来源获得。