1.IO重定向功能分析
IO重定向也是Shell的基本功能之一,这篇文章比较全面地介绍了Linux Shell的IO重定向功能。总结一下,IO重定向的大致格式是这样:
cmd [src | &] (> | < | >> ) (& num|-) | dst
貌似写得有点复杂(我不是故意的)。解释一下,整个重定向语句由三部分组成:
第一部分([src|&]):src代表被重定向的文件描述符,一般是0(stdin)、1(stdout)和2(stderr),此外还可以是‘&’,代表2和3,也就是把stdout和stderr同时重定向。这一部分通常是省略的,‘>’和‘>>’隐含了0,‘<’隐含了1,也就是说
$ cmd file 等价于
$ cmd 1>file
第二部分(> | < | >>):三种操作符。‘<’将src的输入重定向到目标文件;‘>’将src的输出重定向到目标文件(若文件不存在则创建,否则现有文件被截断(truncation));‘>>’将src的输出附加到(pending)目标文件尾部(若文件不存在则创建)。
第三部分( (& num|-) | dst ):第三部分有两种形式,最常见的就是一个文件名,代表重定向的目标。此外还可以是’&’加一个数字,代表将src重定向到数字代表的文件描述符上。另外‘&’后还可以接‘-’,代表关闭src。比如:
$ cmd 2>&1 将标准错误重定向到标准输出
$ cmd 1>&- 关闭标准输出
$ cmd 0<&- 关闭标准输入
2.IO重定向功能实现
Linux的IO重定向实际上是通过dup函数和close函数配合实现。close关闭一个文件描述符,而dup复制一个文件描述符,并且新产生的文件描述符是当前可用文件描述符的最小值。
通过先将要重定向的fd关闭,在将重定向的目标文件的描述符dup就实现了重定向(因为012三个描述符默认都是打开的,关闭任何一个后其都将成为当前可用的最小文件描述符)。
为了支持重定向命令的分析,实现里新添加了一个结构,记录了每个重定向的必须信息,然后在子进程fork以后根据记录信息进行重定向操作。
测试的时候发现了一个有趣的问题。比如:
$ ls >f1 >f2
$ cat >f1 >f2
这两条命令在bash里的结果是后面的重定向覆盖了前面的重定向,这和我的实现是一样的,也是最简单的。而在zsh里,第一条命令的结果是f1 f2文件有相同的内容,也就是标准输出同时被重定向到f1和f2两个文件里了;第二条命令的效果等同于先把f1、f2两个文件现连接起来再重定向到cat的输入。这是相当神奇的,到现在也没找到实现方法,看来得找时间研究一下zsh的代码了。
其实实现重定向最麻烦的地方是命令分析,也就是字符串处理。parse_command函数俨然已经100行了,两个switch-case加无数个if-else组成的大自动机,不加注释恐怕就是write-only了(感觉加了注释还是write-only)...
打开重定向文件那里有一点问题,一大堆flags或来或去,最傻的是忘了给创建的文件设置mode,结果只能用root权限查看...
源代码 : jdsh_v3.h jdsh_v3.c(为了让乱七八糟的代码稍微可读一点,单独分了个头文件出来)
剩下的任务就是管道了,有必要把代码好好整理一下了。
One Response to 自己动手写Linux Shell(三) —— 支持IO重定向