unix v6 shell (2006-07-13修订)
时间:2006-07-14 来源:mhss
* 这个 shell 是 UNIX v6 的 sh 在 POSIX 环境下的重新实现。
* UNIX v6 是受 BSD 许可证保护的自由软件,其中 sh 的原作者是 Ken Thompson。
* 余对这个 shell 的语法做了细节修改,对代码做了重写和注释。
* 余对这个程序和相关文档不做任何担保,放弃一切权利,不承担任何责任和义务。
*
* 最近修订: 2006-07-13 寒蝉退士(http://mhss.cublog.cn)
* 做的工作主要有: K&R C -> ANSI C,unix v6/v7 -> POSIX,去掉进程记帐和 ^,
* 增加了 $?、umask 和 exec,去除了 goto 语句,增加了中文注释。
* 上次修订: 2004-09-06
*/
|
sh.1
Unix 第 6 版的 sh 手册页最后更新: 1974-5-15
名字
sh 外壳 (命令解释器)总览
sh [ -t ] [ -c ] [ name [ arg1 ... [ arg9 ] ] ]描述
Sh 是标准的命令解释器。它是读取大多数用户键入的命令行并安排其执行的程序。它自身可以作为解释命令文件的一个命令来调用。在讨论给作为命令使用的 Shell 的参数之前,先给出命令行自身的结构。命令
每个命令都是由空白分隔的非空白命令参数的一个序列。第一个参数指定要执行的命令的名字。除了后面讨论的特定类型的特殊参数之外,不是命令名字的参数都不经解释的传递给调用的命令。
如果第一个参数是一个可执行文件的名字,则调用它;否则在参数前面前导上字符串‘/bin/’。(在这种方式下可以找到在‘/bin’下驻留的多数标准命令)。如果未找到这个命令,进一步前导上字符串‘/usr’(来给出 ‘/usr/bin/命令名字’)并再次尝试执行结果的文件。(特定的更少使用的命令驻留在‘/usr/bin’)。
如果非目录文件有可执行模式,却不是可执行程序的形式(不以正确的魔数作为开始),则假定它是命令的一个 ASCII 文件并建立一个新 Shell 来执行它。参见后面的“参数传递”。如果不能找到文件,则打印一个诊断消息。
命令行
由‘|’或‘^’分隔的一个或多个命令组成过滤器的一个链条。除了最后一个之外的每个命令的标准输出都被作为下一个命令的标准输入。每个命令都作为一个独立的进程来运行,用管道(参见 pipe(II))与邻近的进程相连接。包围在圆括号‘( )’中的命令行可以出现在简单的命令能作为过滤器出现的任何地方。
命令行由一个或多个独立的并可能用‘;’或‘&’终止的管道线构成。分号指示顺序执行。&’导致执行前面的管道线但不等待它完成。报告这种的管道线的进程 ID,如果有必要的话,后续的 wait 或 kill 可以使用它。
终止报告
如果一个命令(不跟随着‘&’)异常终止,则打印一个消息。(不是 exit 和 interrupt 的所有终止都当为异常)。跟随着‘&’的命令的终止报告,紧随这个命令终止之后的第一个命令的报告之后给出,或者在执行 wait 的时候给出。下面是异常终止消息的列表:
Bus error Trace/BPT trap Illegal instruction IOT trap EMT trap Bad system call Quit Floating exception Memory violation Killed Broken Pipe如果生成了内存映像,添加‘- Core dumped’到适当的消息上。
I/O 的重定向
有三个字符序列导致紧随其后的字符串被解释为给 Shell 自身的特殊参数。这样的参数可以在单一命令的参数中的任何地方,或者在一个括号包起来的命令列表的前面或后面,并与这个命令或命令列表相关联。
‘<arg’形式的参数导致使用文件‘arg’作为相关命令的标准输入(文件描述符 0)。
‘>arg’形式的参数导致使用文件‘arg’作为相关命令的标准输出(文件描述符 1)。如果‘Arg’不存在则建立之,在存在的情况下则截短它。
‘>>arg’形式的参数导致使用文件‘arg’作为相关命令的标准输出。如果‘Arg’不存在则建立之;如果存在,则把命令输出填加到这个文件。
例如,下面的命令行
ls >junk; cat tail >>junk ( ls; cat tail ) >junk
都建立文件‘junk’,它是工作目录的一个列表,随后是文件的‘tail’的内容。
构造‘>arg’或‘>>arg’与管道中除了最后一个之外的任何命令相关联都是无效的,同样‘<arg’只与第一个关联。
在 Shell 调用的命令中,在任何重定向之前文件描述符 2 参照 Shell 的标准输出。这样过滤器可以写诊断信息到有机会被看到的一个地方。
参数列表的生成
如果任何参数包含字符‘?’、‘*’或‘[’的任何一个,则做如下特殊处理。在当前目录查找匹配给定参数的文件。
在参数中的字符‘*’匹配文件名字中任何字符的串(包括空字符串)。
字符‘?’匹配文件名字中任何单一字符。
方括号‘[...]’指定字符的一个类,它们匹配在这个类中的任何单一文件名字符。在方括号内,每个普通的字符都接受为这个类的一个成员。位于类中的由‘-’分隔的一对字符,每个字符在字面上大于等于这个对的第一个成员并小于等于第二个。
其他字符只匹配在文件名中相同的字符。
例如,‘*’匹配所有文件名字;‘?’匹配所有一个字符的文件名字;‘[ab]*.s’匹配开始于‘a’或‘b’并结束于‘.s’的所有文件名字;‘?[zi-m]’匹配 结束于‘z’或从‘i’到‘m’的字母的所有两字符文件名字。
如果带有‘*’或‘?’的参数还包含‘/’,则使用一个稍微不同的处理过程: 不使用当前目录,使用的目录是从参数中选取直到‘*’或‘?’之前的最后的‘/’的获得的。匹配过程针对在导出目录中的文件匹配在这个‘/’之后参数剩余部分。例如:‘/usr/dmr/a*.s’匹配在目录‘/usr/dmr’中的开始于‘a’并结束于‘.s’的所有文件名字。
在任何情况下,获得匹配这个参数的名字的一个列表。这个列表被按字母顺序排序,并用结果的参数序列替代包含‘*’、 ‘[’或‘?’的单一的参数。对每个参数完成同样的处理(结果的列表不合并)并最终用参数的结果列表调用这个命令。
引用
字符‘\’导致紧随其后的字符失去对于 Shell 的所有特殊的意义;这样‘<’、‘>’和对 Shell 有意义的其他字符可以作为参数的一部分来传递。这个特征的一种特殊情况允许命令接续在多于一行之上: 前导着‘\’的换行被转换成一个空白。
包围在双引号(")或单引号(')内的字符的序列也按字面接受。例如:
ls | pr -h "My directory"
导致 ls 生成一个目录的列表,并传递给 pr 来打印并带有标题‘My directory’。引用允许在标题中包含空白,它是给 pr 的一个单一的参数。
参数传递
当 Shell 作为一个命令调用的时候,它有额外的字符串处理能力。回想调用 Shell 的形式是
sh [ name [ arg1 ... [ arg9 ] ] ]
name 是读取并解释的文件的名字。如果未给出, Shell 的这个子实例继续读取标准输入文件。
在文件中的命令行内(不是在命令输入内),‘$n’形式的字符序列,这里的 n 是一个数字,被给 Shell 调用的第 n 个参数(argn)所替代。‘$0’被 name 所替代。
单独使用参数‘-t’,导致 sh 从标准输入读取一行,作为命令执行,并接着退出。这个设施替代老的‘mini-shell’。它用于允许用户执行系统命令的交互程序。
参数‘-c’(与一个随后的参数一起使用)导致下一个参数被接受为一个命令行并执行之。不需要提供换行,但能正确处理换行字符。这个设施用做‘-t’的替代者,这里的调用者已经读取了要执行的命令的某些字符。
文件结束
在 Shell 的输入中文件结束导致它退出。这个事实有一个副作用,从 UNIX 上注销的方法可以是键入一个 EOT。
特殊命令
Shell 对下列命令做特殊处理。
通过执行 sys chdir (II) 来完成 chdir 而不生成新进程。
通过执行/bin/login 来完成 login 而不生成新进程。
通过执行 sys wait (II) 来完成 wait 而不生成新进程。
通过操纵给 Shell 的参数来完成 shift。
简单的忽略‘:’。
命令文件错误,中断
在命令文件执行期间,任何检测到的 Shell 错误,或 interrupt 信号都导致停止这个文件的执行。
用‘&’建立的进程忽略 interrupt。还有,如果这种进程没有用‘<’重定向它的输入,把它的输入自动的重定向到零长度文件 /dev/null 上。
文件
/etc/glob, 它解释‘*’、‘?’、和‘[’。/dev/null 作为文件结束的一个来源。
参见
`The UNIX Time-Sharing System', CACM, July, 1974,它给出 Shell 操作的理论。chdir (I), login (I), wait (I), shift (I)