第5章C-shell的交互功能5.1UNIX的shell5.2csh启动与终止5.3使用csh的历史机制5.4别名5.5csh提示符5.6csh的管道和重定向UNIX系统的重要特性之一就是提供了大量的公用程序,这些程序都经过精心设计,完成一定的功能,作为UNIX的命令。这些命令,在系统中运行时,同程序员自编的应用程序处在相同的地位,都是利用操作系统内核(kernel)提供的系统调用,完成处理任务。在这些公用程序中,有一组被称为外壳(shell)的程序,shell的取名相对于kernel。shell程序是用户和系统之间的接口,是一个交互式命令解释器。用户通过它可以输入命令,然后shell调用这些命令,利用kernel的功能,完成用户的任务。5.1UNIX的shellshell中的许多功能和特点也都来自于kernel,例如,管道、重定向、执行命令创建新进程等。除了可以交互式地输入命令之外,shell还是一种程序设计语言,提供了变量,循环结构和条件结构,用户可以通过它设计批处理程序。UNIX提供了几种shell,主要有:(1)/bin/shB-shell,由StephenR.Bourne在贝尔实验室开发,是最早被普遍认可的shell,也是UNIX的标准shell,设计得非常简练。它的风格被后来出现的其他shell所继承,影响很大。但是,它的命令行编辑功能很弱,交互操作起来非常不方便。(2)/bin/cshC-shell,最先由加利福尼亚大学的WilliamN.Joy(也叫BillJoy)在20世纪70年代开发,最初运用在BSD2.0版本的UNIX,是BerkeleyUNIX的主要特征之一。Joy在1982年和斯坦福大学的三人共同创办了SunMicrosystems公司。C-shell提供了历史机制和别名替换,相对B-shell来说交互起来更方便。在编程方面也更灵活,许多编程结构的风格类似C语言,所以取名C-shell。(3)/bin/kshK-shell,由贝尔实验室的DavidKorn在1986年开发。是B-shell的超集,支持带类型的变量、数组,等等,与sh相比,它提供了更强的功能。(4)/bin/bashBourneAgainshell,这是Linux上的标准shell,它兼容B-shell,并且在标准B-shell上进行了扩展,吸收了C-shell的某些特点。它的命令行编辑方法非常方便,可以直接使用键盘上的上下箭头等全屏幕编辑操作的功能键,便于交互式操作,得到许多用户的喜爱。但是,bash没能在很多其他UNIX中作为标准工具提供。每个用户登录成功后,进入shell。这里的shell种类选择,由系统管理员在创建用户时设置,也可以进行修改。在配置文件/etc/passwd中记录了每个用户的用户名、用户ID、组号、主目录、登录shell等。超级用户直接编辑这个文件,也可以修改用户的注册shell。下面是/etc/passwd中的若干行:tian:x:1289:100::/usr/tian:/bin/cshjiang:x:1306:100::/usr/jiang:/bin/shliang:x:2167:100::/usr/liang:/bin/kshsun:x:1283:100::/usr/sun:/bin/sh像众多的编辑软件一样,每种shell也有自己忠实的爱好者和不认同者。在一般的UNIX中,sh和csh都作为标准命令提供。在本章,介绍csh的便于交互使用的特点,不再介绍csh编程。在第6章中主要介绍标准B-shell编程。所谓的内部命令,就是输入了命令之后,直接由shell“内部”来理解的命令,不需要到系统中搜寻可执行程序文件。有的命令必须设计成内部命令,前面介绍过的命令中,cd命令和umask命令都是内部命令。诸如ls、cp、rm等命令,在UNIX中都是外部命令。既然“内部命令”是由shell解释的,那么,内部命令与具体的shell相关,不同的shell支持不同的内部命令集。外部命令没有这样的限制。有的shell为了提高执行效率,会把一些外部命令实现为同等语义的内部命令。例如:test、echo和expr原来都是外部命令,许多shell把它们实现为内部命令。csh启动时,将自动执行用户主目录下.cshrc文件中命令。如果它作为登录shell运行,再执行主目录中.login文件中命令。不同的用户有自己独立的主目录,所以,不同用户有自己独立的.cshrc文件。这些特性类似DOS中的AUTOEXEC.BAT。许多程序一启动就会搜寻一个批命令文件,这些文件的命名大都以句点开头,如vi的.exrc,mail的.mailrc,B-shell的.profile。作为登录shell的csh终止时,执行主目录下.logout文件中的命令。如果系统不是以csh作为登录shell启动的,那么,直接执行命令csh就可以进入一个交互式的C-shell。5.2csh启动与终止所谓历史机制,指的是csh将最近一段时间内输入的命令保存起来,这样就可以重复使用前面已经输入的命令,或者前面的命令有错时,用一种简化的方法修改,而不需要将命令重新输入一遍。5.3使用csh的历史机制以前键入的命令行被存在历史表中,该表的大小由csh的变量history设定。使用内部命令sethistory=30,可以设定历史表大小为30个命令行。许多系统默认的历史表大小为1。5.3.1历史表大小用csh的内部命令history可列出历史表内容。例如:%history87ls-l/etc/passwd88vi/etc/passwd89ls-l/bin/passwd90manpasswd91vi/etc/security/passwd92cd/usr/include93grep-ntermio*.h94history左侧的编号是命令号,csh为每个命令分配一个编号,csh的提示符一般是%。5.3.2查看历史表引用历史机制的方法,是C-shell进行“历史替换”。与历史替换有关的符号是!。表5-1列举了一些典型的历史机制的引用方法。5.3.3引用历史机制表5-1C-shell历史替换的方法引用方法历史替换操作!!引用上一命令(如同DOS中按F3键)!str引用以str开头的最近使用过的命令。如:用!v引用刚不久执行过的命令vidisp_stat.c命令;用!fin引用最近用过的find.-namecore-print命令;用!cc引用最近用过的ccdisp_stat.c-odisp_stat命令;用!.引用最近用过的./disp_stat命令!45引用历史表中第45号命令!20:s/str1/str2/把历史表中第20号命令中的str1串替为str2后执行。这种方法可以修改一个以前用过的命令^str1^str2把历史表最后一行中str1串替为str2串,并执行。这种方法可以直接修改刚刚键入命令中的错误。例如:刚刚输入的命令find . –nmae core -print,可以用^nmae^name修改!55:2引用55号命令的第二个单词。命令行中的单词从左向右编号0,1,2,…。如果55号命令是上述的find命令。那么命令ls-l!!:3实际上执行ls-lcore!55:^引用55号命令的第一个单词!55:$引用55号命令的最后一个单词!55:2-4引用55号命令的第2~4个单词使用csh的别名机制,可以为一个经常使用的命令取一个别名,帮助提高效率和减少重复劳动。UNIX系统中最常用的命令的命令名长度2~3个字符。可以为那些一段时间内经常使用的命令取一个单字符的别名,简化命令。当然,别名可以是多个字符。与别名有关的csh内部命令是alias和unalias。5.4别名csh的内部命令alias用于定义一个别名。例如:aliash'history'alaist'tail-f/usr/adm/pppd.log'aliasl'ls-Fl'aliasrm'rm-i'aliasdir'ls-Flad'aliastype'cat'aliasn'netstat-ptcp-s|head-10'aliasr'netstat-rn'aliasp'ping202.143.12.189'aliasrt'traceroute217.226.227.27'5.4.1在别名表中增加一个别名通过定义一些别名,可以不用输入这么复杂的命令。上面的rm被定义为别名,从而,在csh中键入rm已经不再是原来意义上的rm,在这里对rm重新进行了定义。利用命令的同名别名进行重定义功能,带来了很多方便。当用户发现某个命令执行起来和从前不同,有必要检查别名表,看看是否已经通过“别名替换”被偷梁换柱。用户自己定义的别名,会提高工作效率。然而,这些别名设置在csh中止后就不再存在。最好将这些别名设置,连同前面介绍的sethistory=30这样的命令放到.cshrc文件中,每次执行csh时重新设置。使用不带任何参数的alias命令,能打印出当前的别名表。5.4.2查看别名表许多命令带有参数,使用别名带有参数时,就将参数传递给命令。例如:aliasdir'ls-Flad'dir~那么,实际执行ls-Flad~在csh中,~是个特殊符号,它将被csh展开为当前用户的主目录。这种处理是csh的处理,其他的shell未必会这样做。这种简单的参数传递很容易理解,需要传递的参数都排列在别名定义之后。但是,有时这种方法并不能满足用户的需求。5.4.3给别名传递参数【例5-1】给别名命令传递参数的方法。下面的命令递归式地在系统头文件目录下检索所有的头文件,查找含有termio字符串的程序行。find/usr/include-name*.h-execgrep-ntermio{}/dev/null\;|more键入这么长的一串命令很费力气,而且,一段时间内会经常用到这个命令,定义一个别名用起来简便。但是,每次检索的字符串不同。这次检索termio,下次检索的是tcp_hdr,或者别的字符串。使用上述的简单别名无法达到要求。别名参数的使用,采用和历史机制相同的方式。用一个惊叹号表示当前的输入。和历史命令一样,在冒号后边的数字或者^号$号代表参数号。aliasf'find/usr/include-name*.h-execgrep-n\!:${}/dev/null\;|more'这里的惊叹号前面加上转义符\,使得csh不再把他们解释为历史替换,而是把惊叹号作为一个字符,传递给alias命令,alias真正得到的是!:$。这样,有了别名,直接使用ftermio就可以了。也可以传递多个参数。aliasscan'find\!:3-name*.h-execgrep-n\!:1{}/dev/null\;|more'引用时,使用下面的命令能达到前面的命令相同的效果:scantermioin/usr/include使用内部命令unalias,可以取消别名。用法是:unalias别名例如:unaliasn在别名表中取消别名n5.4.4取消别名csh的提示符默认是%,用户可以自己使用csh的变量prompt控制,自定义csh提示符。prompt变量值是一个字符串,在这个字符串中的!,shell实际显示命令提示符时会以命令号代替。setprompt=[\!]%在!前加\以取消csh对!的特殊解释,对变量prompt赋值为由4个字符构成的[!]%字符串。5.5csh提示符在交互式命令中,经常会使用到管道和重定向。标准输入和标准输出的输入重定向以及管道,在不同的shell中,用法都相同。例如:ls-l|moremanlsman.papermanrmman.papertrUVWuvwfile1file2重定向符号是向文件中追加。除了标准输入和标准输出的输入重定向和管道之外,标准错误输出的重定向在不同的shell之间有所不同。5.6csh的管道和重定向例如:C语言的程序员很容易会发现标准错误输出和标准输出的不同之处。ccmyap.c-omyap|more这样