linux之标准输入输出
IO
要弄清什么是标准输入输出。首先需要弄懂什么是IO。
IO的I是Input的意思,O是output的意思。
意味着输入和输出。
更确切的含义是
I:从外部设备输入到内存
O:从内存输出到外部设备
标准输入、输出
linux中一切设备皆是文件!
因此标准输入和输出更具体的含义是文件。
它们是/dev/stdin这个文件和/dev/stdout这个文件。
也就是说所谓的标准输入和标准输出其实就是两个linux下的文件。
linux的文件类型有:
1、普通文件2、字符设备文件3、块设备文4、目录文件
5、链接文件6、管道文件7、套接字文件
标准输入逻辑上来看:
就是打开/dev/stdin这个文件,然后把这个文件里的内容读进来。
输出到标准输出逻辑上来看:
就是打开/dev/stdout这个文件,然后把内容输出到这个文件里去。
为什么是从逻辑上来看?因为它们不是设备文件!!!
所以它们不代表一个设备。linux里一切皆是文件,设备是文件,但是文件不一定是设备!
那它们是什么文件?他们是链接文件。(可以用ls -l /dev来查看 l开头的就是链接文件。)
文件内容是另一个文件的地址的文件称为链接文件。
因此,打开、读或者写 /dev/stdin和/dev/stdout 实际上是打开、读或者写这两个文件存放的地址对应的设备文件。
文件描述符
linux进程每打开一个文件都会返回一个文件描述符(整数)。
这个描述符实际是打开的文件在该进程的描述符表上的偏移值。
比如说p是描述符表,1是描述符,那么p[1]就能够索引到1描述符对应的打开文件。
有了这个偏移值(文件描述符)就能够快速的找到并操作文件。
(当然实际的情况是这个文件描述符能够索引到打开文件表表项,然后再通过打开文件表表项索引到对应的V-node节点表表项,而这个v-node节点表表项才代表真正的文件。不过只从逻辑上来看不需要理解这个括号里的说明。)
文件 | 文件描述符 | 链接文件 | 文件 |
---|---|---|---|
输入文件—标准输入 | 0(缺省是键盘,为0时是文件或者其他命令的输出) | stdin | /proc/self/fd/0 |
输出文件—标准输出 | 1(缺省是屏幕,为1时是文件) | stdout | /proc/self/fd/1 |
错误输出文件—标准错误 | 2(缺省是屏幕,为2时是文件) | stderr | /proc/self/fd/2 |
linux重定向标准输入说明
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd1,fd2;
fd2 = open("p1.py",O_RDONLY,0);
dup2(fd2,0);
char c;
while( read(0,&c,1) > 0)
printf("%c",c);
close(fd2);
fd1 = open("/dev/stdin",O_RDONLY);
printf("%d\n",fd1);
while( read(fd1, &c, 1) > 0)
printf("%c",c);
return 0;
}
程序解释
1、首先打开了一个叫做p1.py的文件。
2、然后用dup2这个函数使得文件描述符0这个位置的指针指向文件描述符fd2这个位置的指针指向的文件。
也就是说本来是这样:p[0] = &fiel1,p[fd2] = &file2,现在p[0] = p[fd2] = &file2。
而我们都知道文件描述符0这个位置对应的文件file1是标准输入文件/dev/stdin。
那么这个函数的意义就是把标准输入重定向到了p1.py这个文件里了。
之后再用标准输入比如说scanf(),来读,那么都是从p1.py这个文件里读了。
3、然后把这个文件里的东西读出来并输出到屏幕上。
4、打开/dev/stdin这个文件,这个文件是标准输入。
5、把这个文件里的东西读出来并打印到屏幕上。
预期结果
执行到第5步应该停下来,等待键盘输入。
然后把输入的东西打印到屏幕。
实际结果
没有等待键盘输入,它直接把p1.py的文件内容输出到屏幕上,也就是说和上面的输出是一样的!!!
原因
这是因为,标准输入这个文件/dev/stdin是个链接文件!!!
它存放的是别的文件的地址!!!
如果文件描述符指向的文件是个普通文件,那么把这个文件描述符指向别的文件,就是真的指向了别的文件。
而这里的文件描述符指向的是个链接文件,那么把这个文件描述符指向别的文件,意味着什么???意味着它
把这个链接文件里的内容(地址)更改了,而它仍然指向这个文件。只不过它知道这是个链接文件,因此它会访问的是这个链接文件中的地址对应的文件。
理解了上面的操作就可以说得通了,dup2对于链接文件只是修改了文件中的地址,它并没有真正指向别的文件,这也导致了一个问题,那就是它把这个链接文件给修改了,如果进程再次打开这个链接文件,那么链接文件之前存的地址就没有了,因此执行
fd1 = open("/dev/stdin",O_RDONLY);
这个的时候,实际上是又一次打开了p1.py这个文件!!!因为/dev/stdin这个文件里存放的地址已经是p1.py这个文件的地址了。。。
标准输入输出重定向
绑定重定向
- Command >&m
把标准输出重定向到文件描述符m中 - Command < &-
关闭标准输入 - Command 0>&-
同上
输出重定向
- Command > filename
把标准输出重定向到一个新文件中 - Command >> filename
把标准输出重定向到一个文件中(追加) - Command > filename
把标准输出重定向到一个文件中 - Command > filename 2>&1
把标准输出和错误一起重定向到一个文件中,2>&1表示将错误输出绑定到文件描述符1上,1即标准输出 - Command 2 > filename
把标准错误重定向到一个文件中 - Command 2 >> filename
把标准输出重定向到一个文件中(追加) - Command >> filename2>&1
把标准输出和错误一起重定向到一个文件(追加) - command 2> /dev/null
如果command执行出错,将错误的信息重定向到空设备
标准输入重定向
- Command < filename > filename2
Command命令以filename文件作为标准输入,以filename2文件作为标准输出 - Command < filename
Command命令以filename文件作为标准输入 - Command << delimiter
从标准输入中读入,知道遇到delimiter分界符