西西河

主题:【原创】基于Linux内核的开放源代码操作系统的组成:第一篇 -- 请尽量

共:💬75 🌺106
全看分页树展 · 主题 跟帖
家园 【原创】基于Linux内核的开放源代码操作系统的组成:第二篇

第二篇:命令行界面

以前一提到UNIX,大家自然就会想到命令行界面和那些稀奇古怪的命令名:ls、rm、fsck、dd、ln、tar、more、less、cpio、grep、sed、man等等。基于Linux的操作系统无一例外地继承并发扬光大了这个feature(或是bug,取决于你站在线的那边)。

在进一步深入讨论命令行之前,我们先来看一下Linux的几个核心概念:文件、目录树和文件系统。在Linux看来,几乎所有的操作系统资源都表达为一个一个的文件。每个文件都有一个名字。你的简历是一个叫“resume.txt”的普通文件(regular file),其所在的“Documents”文件夹是一个目录文件(directory),你那个用了三年的IBM硬盘是个块设备文件(block device),上面的每个分区也都是块设备文件,你的PS/2鼠标是个字符设备文件(character device),你刚刚买的USB外接硬盘在连上以后,也是一个块设备文件。除此之外,你可以产生命名管道文件(named pipe)用于进程间通信,还可以生成(symbolic link)或(hard link)指向另一个文件名。除了类型外,每个文件还有一个属性表,包括文件的字节数、其的所有者(用户和组各一个)、存取权限(记住Linux是个多用户系统)、最后一次修改的时间、最后一次访问的时间等等。这些称作“传统”的文件属性。近来,Linux又从SGI等处借鉴了“extended attributes(简称为EA)”这么个概念。在支持EA的文件系统上,每个文件可以有的属性类型和数量都是近乎无限的。比方说,你可以给你的相片加上长长的注解,甚至配上语音的解说。当然,内核不关心具体各个EA的名字和类型,具体的解释留给了各个应用软件。内核只提供一套API用于存取这些EA。

目录在其中最为特殊,因为除了一个文件外,所有的文件(包括目录),必须存在于某个目录中。这样,所有的文件就组成了一个目录树。这棵树的根,就是那个搞特殊化的唯一不存在于任何一个目录中的文件,按惯例称作根目录(root directory),用一个斜线(/)表示。一个文件在这棵树上的位置,可以表达为从根开始所经过的所有目录,又称为该文件的绝对路径名。例如,“/home/qingjinliang/Documents/resume.txt”就是一个绝对路径名,其中,“/home/qingjinliang/Documents”是dirname部分,resume.txt是basename部分。该绝对路径表示在经过根(/)、/home、/home/qingjinliang、/home/qingjingliang/Documents四级目录后,有一个名为resume.txt的文件。由树这种数据结构的特点,我们可以知道:如果要使两个文件具有相同的绝对路径名,那么它们必续存在于同一个目录中,并且有相同的文件名。所以,只要保证在同一个目录下没有相同的文件名,那么我们就可以保证每一个文件都可以由其绝对路径名唯一确定。另外,树上的任何一个目录和这个目录下的内容,也可以看成一个以该目录为根的目录树。

经过这样的抽象,对这些系统资源的操作,可以统一为对文件的操作:产生(creat)、打开(open)、读(read)、写(write)、定位(lseek)、上锁(flock)、关闭(close)、删除(unlink)、改变存取权限(chmod)、改变所有者(chown)等等。当然,每一种具体的文件类型并不支持所有的操作,另外还可以有其特殊的操作。例如,symbolic link没有内容,当然也无法进行读和写操作,但是可以读出其指向的文件名(readlink)。

由于用户大部分的时间都是在与文件打交道,必须在任何时间都清楚自己在目录树上的位置。Linux的每一个用户进程都有一个当前工作目录的属性,可以通过系统调用getcwd得到。方便起见,访问在当前工作目录下的文件不需要使用绝对路径。自然的,如果当前工作目录下包含另一个目录,那么从当前目录访问那个目录下的文件也不需要绝对路径。这样推广开去,我们就有了相对路径的概念,也就是任何由当前工作目录出发的路径。Linux用“.”代表当前工作目录,“..”代表当前工作目录的上一级。

现在回到主题,命令行界面。从上一篇我们知道如果在字符界面下登录进系统后,系统会运行一个shell程序作为用户和系统的接口。这个shell程序接受用户的输入作为命令来执行,并将命令的输出反馈给用户。

在Linux系统下可供选择的shell有bash、ksh、csh等。在桌面系统和服务器上,bash因为功能强大,容易扩展,一般是首选。但缺点是体积庞大、速度较慢。bash最让人称道的一个功能是tab completion。类似于IDE编辑器中的auto completion,在用户按下tab键后,bash可以根据当前命令行的context(上下文环境?来龙去脉?),尝试complete正在编辑的命令或命令的参数。比方说,系统上安装了一个非常fancy的HTML编辑器,你用它来编辑所有的HTML文件。但是该程序的名字很不幸地被安装程序弄成了“cool-html-editor”。如果使用bash,那么你不用每次都输入那么长的程序名。当你在提示符后输入“cool”后,就可以用tab建来提示bash进行completion了。如果在你的程序搜索路径下只有一个可执行文件是以“cool”起头的,bash会自动把“-html-editor”补在“cool”后,给你一个完整的名字“cool-html-editor”。

别急,事儿还没完呢。如果你要编辑一个已有的文件,你可以接着使用tab completion。现在,bash知道你是要一个文件名,而不是一个程序名。bash会在你的当前工作目录下根据你给出的提示进行匹配。比方说,你要编辑的文件名是“Linux-intro.html”,那么在“cool-html-editor”后输入一个空格,跟着“Linux”,然后再用tab。bash会找出当前工作目录下所有的以“Linux”起头的文件名,如果只有一个文件,那么bash会自动在命令行把“-intro.html”补上。如果有多个文件,bash会列出所有的候选。只要输入足够长的起头字符串,再使用tab,如此反复,最终可以得到你所要的文件名。

在两次使用tab completion时,bash正确地从命令行的上下文环境中判断出第一次需要一个程序名,第二次需要一个文件名,然后去相应的地方搜索、匹配。更妙的是,bash的这个功能可以通过plugins进行扩展。比方说,在“cool-html-editor”后紧接着输入tab,bash知道这个程序需要一个HTML文件名,会相应地进行筛选,列出当前工作目录下所有的HTML文件作为候选。在安装了所需要的plugins之后,tab completion还可用来完成特定程序的命令行选项及参数。

除了tab completion,bash的命令行还支持完整的编辑功能。基本上,在bash下输入命令行就象在一个只有一行高的窗口中编辑文本,用左右光标键可以在命令行上左右移动。bash也支持命令行历史,可以用上下光标键浏览以前执行过的命令行。你甚至还可以在命令行中以起头字符串进行查找。

在Linux系统的命令行上,一个命令的执行可以被放到后台。比方说,你估计压缩一个庞大的文件需要十分钟,那么在命令行的最后加上一个“&”再按下回车键。这样,bash在把压缩程序放到后台运行后就立刻返回,又可以接受你的下一个命令了。当那个压缩程序运行完以后,bash会自动在屏幕上打印出提示,提醒你可以去检查其运行结果了。

另一个非常有用的功能是命令行管道。在Linux系统下,一个命令的输出可以作为另一个命令的输入,把两个命令用“|”相连,就构成了一个管道。如果把一个管道看作是一个命令,那么这个管道可以通过另一个“|”延长,直到超出命令行的编辑长度限制。管道中的每个命令就象一个过滤器,对自己的输入(也就是上一个命令的输出)进行处理。要注意这里的“上一个”和“下一个”是指的命令在管道中的位置,不是命令行历史中时间上的概念。命令行管道的存在使得很多程序只需要做一件事,简单,但是可以做得尽善尽美,并且这些简单的命令可以灵活组合构成满足各种需要的命令行管道。这简直就是“分而治之”的一个完美的运用。用命令行管道组合多个简单的命令完成复杂的工作已经成了UNIX命令行的一个标志性的特点。

bash不仅仅在命令提示符后接受命令。如果需要反复执行一系列命令以完成一件工作,那么这些命令可以存在一个文件里,然后让bash去执行这个文件。bash不仅支持顺序执行命令,还有分支、循环等程序控制逻辑,加上变量(无类型)、数组、函数(过程)和递归,就有了一个功能强大的解释型scripting language。事实上,bash的程序功能是如此的强大,基于Linux的操作系统的很多对性能要求不高的部分都是用bash scripts实现的。

上面我们提到用户在使用命令行时,大部分的时间都是在和文件打交道。由于文件是由绝对和相对路径名来确定的,绝大部分的命令行程序都接受路径名作为命令行参数,当对多个文件进行操作的时候,模式匹配(pattern matching,又称globbing)提供了很大的方便:“*”匹配任何字符串,包括空串,“?”匹配任意一个字符,“[0-9]”匹配从“0”到“9”的任意一个数字,“[ch]”匹配字母“c”或“h”。举个例子:“ch*[1-8].[hH]tml”匹配“ch1.html”、“ch1.Html”、“chapter8.html”、“ch18.html”,...

最后,要注意Linux常用的文件系统都是对大小写敏感的。也就是说,文件名“file.txt”、“File.txt”、“FILE.TXT”是各不相同的。另外,大部分的文件系统都支持UTF-8作为文件名的编码。所以,可以给文件起个中文名。最后,Linux的文件名没有后缀的概念,虽然习惯上人们还是会给文件名加上表示格式的后缀。Linux有另外的办法区别文件内容的格式。

作为Linux的“标准”配置,常用的命令行程序包括:

* 列出指定目录下的文件:ls

大概是list的简写。如果没有给出目录名,就意味着是要当前工作目录, 这也是很多命令行程序的缺省行为。“.”在Linux的文件名中还有一个特殊的用途:ls会跳过以“.”起头的文件名,包括“.”和“..”。这相当于Windows下的隐含文件属性。如果你使用的终端设备或终端模拟程序支持彩色输出,ls还会使用不同的颜色显式不同类型的文件,让你做到一目了然。只要你的终端设备或终端模拟程序不是太旧,应该都支持彩色输出。

* 猜测文件内容格式:file

为什么说是猜呢?因为在Linux的文件系统下,无法从文件名上看出文件的内容格式,文件的属性表也没有内容格式一项。好在绝大部分的文件格式都会在文件开头的几个或几十个字节中存放一个独特的标志。但是由于文件的格式实在太多了,还有很多可能压根是垃圾的东西充斥其中,无法保证标志的“唯一”,那么,只好猜了。所幸的是,file这个命令猜得还是八九不离十的。

* 复制文件:cp

大概是copy的缩写。不仅可以复制普通文件,还复制目录和目录下的内容,也就是递归地把一个目录树复制下来。

* 搬动文件:mv

move的缩写。把一个文件从一个目录下搬到另一个目录下,或者,在同一个目录改变文件名。从上面对文件名、路径名和目录树的描述,我们可以知道这两种操作其实是一致的。不象DOS,Linux没有rename的概念。

* 删除文件:rm

remove的缩写。不仅可以删除单个的文件,这个命令还可以递归地删除整个目录树。要特别注意的是,在命令行下是不能undelete的。由于Linux是一个多用户多进程的操作系统,原来被那个删掉的文件所占用的硬盘存储空间有可能马上就被其他的用户,甚至是当前用户自己的其他进程给用掉了。When it's gone, it's gone, for ever。在一个繁忙的系统上,特别是当有些应用程序频繁写盘时,这种可能性是很大的。

*打包和解包:tar

Tape ARchive的缩写。最早是用于写磁带设备的。现在基本上是标准的源代码发行格式。可以把一个或多个文件或目录树装进包里,再在需要的地方释放出来,重建目录树。由于在包里使用的是相对路径名,可以在临时目录下释放包的内容,而不用担心覆盖已有的文件。“tar -c”用于打包(create),“tar -x”用于释放包里的内容(extract),“tar -t”用于列出包里的内容。tar命令生成的包文件的后缀名通常是“.tar”。tar命令其实并不关心,你可以给任何文件名。

* 压缩:gzip、bzip2、zip

常用的几种压缩程序。gzip的压缩率不错,速度快,需要的内存少,支持流压缩(不需要把源数据全部读进来再压缩,也就是可以用在命令行管道的下游)。惯例上,经过gzip压缩的文件以“.gz”结尾。一个例外是如果压缩的文件是个tar文件,那么可以把后缀改为“.tgz”。大概主要是为了和Windows和CDROM的文件格式兼容吧。bzip2压缩率高,但是速度低,也支持流压缩,出现的时间不如gzip长因而不及gzip普遍。文件名后缀通常是“bz2”。zip的压缩率似乎最低,但是兼容Windows下最常见的WinZIP格式,所以也是一个常用的工具。

如果给gzip或bzip2一个“-d”选项,就变成了解压。解压也支持流操作,例如“gzip -d linux-2.6.9.tar.gz | tar -xf -”

* 在文件内容中匹配字符串:grep

这个怪怪的名字的来历也很怪:来自于一个行编辑器ed的命令“g/re/p”,表示“Globally search for the Regular Expression and Print”。基本上,grep在指定的文件中匹配所给的正则表达式,并把匹配的行输出。例如:“grep ‘[lL]inux’ *.html”在所有以“.html”结尾的文件中,找出包含“Linux”或“linux”的所有行。grep所支持非常强大的正则表达式匹配,是shell scripting常用的命令之一。

* 在目录树中找文件:find

这个命令在指定的目录树中,找出所有满足给出的文件名或其他属性条件的相对路径名。例如:命令“find /usr/lib -name 'libc*' -type f”会找出目录“/usr/lib”下所有的以“libc”起头的,类型是普通文件的路径名。

* 输出文件的内容:cat、more、less

cat是concatenate的简写。这个命令的用途之一是把多个文件“串联”输出到进程的标准输出(stdout)上,因而得到这么个名字。由于cat不管文件有多大,一古脑儿地把文件内容灌到屏幕上,让人目不暇接。所以在实际使用中cat一般只用来输出小文件。

more和cat的区别在于more会考虑到文件的行数,在输出占满一屏后会暂停下来,在屏幕的最底下打出一行“--More (16%)--”,等着用户按下空格键或回车键继续。这就是more这个名字的来源。less比more还要好。less还支持向后滚屏,这样你就不用担心错过了重要的部分。当然,由于less做得最多,需要的资源如内存等也最多。cat基本上没做什么,也基本上不用什么资源。

* 文档:man、info

“man”是manual的缩写。例如,要想知道ls命令的所有选项,用“man ls”。man会自动调用less来对输出分页、暂停。通常man的输出所包含的信息就足够了。但有些复杂的命令的完整的文档是用info格式写的。

* 编辑文本:vi

vi是“VIsual editor”的缩写,相对于上面所提到的命令行编辑器ed而言的。vi支持全屏幕的编辑。现在几乎所有的vi用户都在使用一个称为Vim(Vi IMproved)的程序。Vim相对vi做了很大的改进,有关vi和Vim的使用足够写很厚的一本书了。

当然,Linux还有很多其他的命令行程序,但是一个普通用户在命令行下每天要用到大概就是这些。每个人都有自己使用Linux的目的和习惯,所需要的工具当然也就不同了。

全看分页树展 · 主题 跟帖


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河