9.3 Bourne Again Shell与TC Shell的共性
大多数bash和tcsh相同的特性都源自于原C Shell:
● 命令行扩展(也称为替换)
● 命令历史
● 别名
● 作业控制
● 文件名替换
● 目录栈操作
● 命令替换
因为关于bash的章节已经详细讨论了这些特性,所以本章将关注bash和tcsh实现上的差别。
9.3.1 命令行扩展(替换)
关于Bourne Again Shell中的命令行扩展的介绍请参见第8.9节。tcsh的man页中使用“替换”而不是“扩展”(在bash中使用这个词)。TC Shell按照下面的顺序扫描每个词,寻找可能存在的扩展:
① 历史替换
② 别名替换
③ 变量替换
④ 命令替换
⑤ 文件名替换
⑥ 目录栈操作
1. 历史机制
TC Shell为每个命令行分配一个顺序的事件编号。可以把这个编号作为tcsh提示符(参见第9.6.7节中的prompt部分)的一部分显示出来。在本章的示例中,如果事件编号有助于说明命令的行为,就会显示编号之后的提示符。
(1) 内置命令history
与在bash中一样,tcsh内置命令history显示历史列表中的事件。事件列表按照最老的事件排在最上面的顺序排列。历史列表中的最后一个事件就是显示这个列表的history命令。下面的历史列表中,这条history命令的参数10将这个列表的大小限制在10行,命令行23修改了tcsh提示符使其显示历史事件编号。每条命令执行的时间显示在事件编号的右边。
32 $ history 10
23 23:59 set prompt = "! $"
24 23:59 ls -1
25 23:59 cat temp
26 0:00 rm temp
27 0:00 vim memo
28 0:00 lpr memo
29 0:00 vim memo
30 0:00 lpr memo
31 0:00 rm memo
32 0:00 history
(2) 历史机制扩展
在两种shell中同样的事件和字标志符都可以使用。举例来说,!!在tcsh中表示前一个事件,这与在bash中是一样的。命令!328执行编号为328的事件,而!?txt?执行最近的包含字符串txt的事件。更多信息请参见第8.5.2节中的“使用感叹号(!)引用事件”部分。表9-1列出了bash中没有的几个tcsh字修饰符。
表9-1 字修饰符
|
修 饰 符 |
功 能 |
|
u |
将第1个小写字母转换为大写 |
|
l |
将第1个大写字母转换为小写 |
|
a |
把紧接a的修饰符应用到单字中的每个字母 |
在一条命令中可以使用多个字修饰符。举例来说,修饰符a与修饰符u和l连用时,可以改变整个字的大小写。
tcsh $ echo $VERSION
VERSION: Undefined variable.
tcsh $ echo !!:l:a1
echo $version
tcsh 6.12.00 (Astron) 2002-07-23 (i386-inte1-1inux) options 8b,n1s,...
除了使用事件标志符来访问历史列表之外,还可以使用命令行编辑器来访问、修改和执行前面的命令。
(3) 变量
在tcsh中用来控制命令历史机制的变量与在bash中使用的有所不同。在bash中使用变量HISTSIZE和HISTFILESIZE来决定会话期间和会话之间预留的事件数目,而tcsh使用history和savehist(如表9-2所示)。
表9-2 历史变量
|
变 量 |
默 认 值 |
功 能 |
|
history |
100个事件 |
在一个会话期间保存的最大事件数目 |
|
histfile |
~/.history |
历史文件的位置 |
|
savehist |
未设置 |
在会话之间保存的最大事件数目 |
history和savehist 当从tcsh shell中退出时,最近执行的命令会被保存到~/.history文件中。在用户下次登录时,就会用这个文件来初始化历史列表。savehist变量的值决定了保存在.history(没有必要与history变量一样)文件中的行数。如果没有设置savehist,tcsh将不会在会话之前保存命令历史。history和savehist变量必须是局部变量(用set声明,而不是setenv)。history变量保存着在一次会话期间被记录的事件的数目,而savehist保存着会话之间记录的事件数目。具体情况请参见表9-2。
如果将history的值设置得过高,那么历史列表将会占用太多的内存。如果没有设置或者设置为零,shell将不会保存任何命令。为了建立保存500条最近事件的历史列表,可以手动输入下列命令或者将其放在~/.tcshrc初始化文件中:
tcsh $ set history = 500
下面的命令将使tcsh在登录会话之间保存最近的200个事件:
tcsh $ set savehist = 200
可以将这两条赋值语句合并到一个命令中:
tcsh $ set history=500 savehist=200
在设置了savehist之后,当用户注销并再次登录时,前一次登录会话中的200个最近事件将出现在该用户的历史列表中。如果希望在两次登录之间维护事件列表,就需要在~/.tcshrc文件中设置savehist。
histlit 如果设置了histlit变量(history literal),那么history将把历史列表中的命令按照它们被输入时的形式显示出来,不做任何shell解释。下面的示例给出了这个变量的效果(注意比较编号为32的两行):
tcsh $ cat /etc/csh.cshrc
...
tcsh $ cp !!:1 ~
cp /etc/csh.cshrc ~
tcsh $ set histlit
tcsh $ history
...
31 9 35 cat /etc/csh.cshrc
32 9 35 cp !!:! ~
33 9 35 set histlit
34 9 35 history
tcsh $ unset histlit
tcsh $ history
...
31 9:35 cat /etc/csh.cshrc
32 9:35 cp /etc/csh.cshrc ~
33 9:35 set h-istlit
34 9:35 history
35 9:35 unset histlit
36 9:36 history
选读
bash和tcsh在扩展历史事件标志符方面存在着一些差异。如果给出命令!250w,那么bash将用编号为250的命令替换这条命令,并在其后追加一个字符w。相反,tcsh向后检查历史列表,查找以字符串250w开头的事件。存在这个差别的原因是:bash将250w的前三个字符解释为某个命令的编号,而tcsh将这三个字符作为搜索串250w的一部分。(如果只有250的话,tcsh就会将其作为一个命令编号。)
如果想在命令编号后面追加w,可以用花括号将事件编号与w隔离开:
!{250}w
2. 别名
tcsh中alias/unalias特性与bash中的对应特性比较相似。然而,内置命令alias的语法稍有不同:
alias name value
下面的命令创建了ls的一个别名:
tcsh $ alias ls "ls -lF"
tcsh的alias允许替换命令行参数,而bash的alias不能:
$ alias nam "echo Hello, \!^ is my name"
$ nam Sam
Hello, Sam is my name
别名中的字符串\!*扩展到所有命令行参数:
$ alias sortprint "sort \!* | lpr"
下面的别名显示了它的第2个参数:
$ alias n2 "echo \!:2"
(1) 特殊别名
有些称为特殊别名的别名名称对于tcsh来说有着特殊的意义。如果使用这些名称定义别名,tcsh将按照表9-3中的解释自动执行。在初始状态下,所有特殊别名都是未定义的。
要想查看当前的别名列表,可以使用命令alias。而要想查看某个特殊名字的别名,可在alias命令后面跟着该名字。
表9-3 特殊别名
|
别 名 |
执 行 时 间 |
|
beepcmd |
每当终端正常响铃时;在这个时候,用户有机会使用其他视觉或者声音效果 |
|
cwdcmd |
每当改变到另一个工作目录时 |
|
periodic |
周期性地执行,执行频率取决于tperiod变量定义的分钟数;如果没有设置tperiod,或者其值为0,那么periodic就没有意义 |
|
precmd |
刚好在shell显示提示符之前 |
|
shell |
给出shell的绝对路径名,用户打算使用该shell运行那些不以#!开头的脚本 |
(2) 别名中的历史替换
可以使用历史机制替换命令行参数,其中单感叹号表明该命令行包含别名。替换时使用的修饰符与history使用的修饰符相同。在下面的示例中,感叹号被转义,这样shell在建立别名的时候就不会解释它们:
21 $ alias last echo \!:$
22 $ last this is just a test
test
23 $ alias fn2 echo \!:2:t
24 $ fn2 /home/jenny/test /home/alex/temp /home/barbara/new
temp
事件21定义了一个名为last的别名,它可用来显示最后一个参数。事件23定义了别名fn2,显示命令行上的第2个参数的简单文件名或者末尾。
9.3.2 作业控制
作业控制在bash和tcsh中相似。用户可以在前台和后台之间移动作业,可以临时挂起作业,并且可以获得当前作业的列表。%字符后面跟着一个作业编号或者一个唯一标识某个作业的字符串前缀,这样就可以引用这个作业。当在这两个shell中分别运行多进程命令行的时候将会看到一些小的差别:tcsh显示属于某个作业的所有进程的PID编号,而bash只显示每个作业的最后那个后台进程的编号。第8.2.6节中的那个示例在tcsh下就像下面所显示的样子:
tcsh $ find . -print | sort | lpr & grep -1 alex /tmp/* > alexfiles &
[1] 18839 18840 18841
[2] 18876
9.3.3 文件名替换
与bash一样,TC Shell也在路径名中扩展字符*、?和[ ]。*字符匹配包含零个或者多个字符的字符串,?匹配任意单个字符,而[ ]定义字符类。字符类可用来匹配出现在一对括号中的单个字符。
TC Shell将以代字符(~)开头的命令行参数扩展为文件名,扩展方法与bash大致相同,其中~代表用户的主目录,而若代字符后面还跟着某个用户的用户名,则表示该用户的主目录。bash中的特殊扩展~+和~-在tcsh中不可用。
花括号扩展在tcsh中可用。与代字符扩展一样,即使花括号扩展能够产生并不是实际文件的名称的字符串,它仍被认为是文件名替换的一个方面。
在tcsh及其前辈csh中,用模式匹配文件名的过程被称为通配匹配,而该模式称为通配模式。如果tcsh不能识别与某个通配模式匹配的一个或者多个文件,它就会报告一个错误(除非该模式包含花括号)。设置shell变量noglob将抑制文件名替换,包括抑制代字符和花括号的解释。
9.3.4 操作目录栈
在tcsh下的目录栈操作与在bash中差别不大。内置命令dirs显示目录栈的内容,内置命令pushd和popd分别往目录栈压入目录和从目录栈弹出目录。
9.3.5 命令替换
在tcsh中并没有$(...)格式的命令替换。此时必须使用原来的`...`格式。除此之外,这个功能的实现在bash和tcsh中都是一样的。参见第8.9.4节中的“命令替换”部分以获取更多关于命令替换的信息。






