自己动手写Linux Shell(三) —— 支持IO重定向

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(为了让乱七八糟的代码稍微可读一点,单独分了个头文件出来)

剩下的任务就是管道了,有必要把代码好好整理一下了。

This entry was posted in 技术学习 and tagged , . Bookmark the permalink.

One Response to 自己动手写Linux Shell(三) —— 支持IO重定向

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据