一. 重定向命令
1. 在shell中运行命令时,系统为每个进程自动打开三个文件描述符。
输入重定向 :more
输出重定向: cat file>result.txt
追加重定向: ls –l>>list.txt
错误重定向 :./myfile 2>err.txt
2. 重定向的实施者:从程序和执行的命令来看,重定向并不是由命令实现的,是由shell实现的,其 参数中并没有重定向符号和文件名,而且 重定向并不仅限于命令末尾。
3. 实施重定向的3个条件:
第一,标准I/O文件对应于最小的三个文件描述符
第二,最低可用文件描述符原则 即:当打开文件时,为此文件安排的描述符总是可用的文件描述符中值最小的。
第三,exec函数并不影响执行前打开的文件描述符集合。
4. 实现重定向
//重定向方法一: close then open
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int fd;
char buf[80];
read(0,buf,80);
write(1,buf,80);
close(0);
fd=open("./tmp.txt",O_RDONLY);
if(fd != 0){
perror("open");
exit(1);
}
read(0,buf,80);
write(1,buf,80);
return 0;
}
//重定向方法二: open…close…dup…close
dup函数: 功能:复制一个文件描述符
定义:int dup ( int oldfd );
参数:oldfd 表示将要复制的文件描述符
返回值:-1,出错; >-1,表示新的文件描述符
dup遵循最低文件描述符原则,新复制的文件描述符和oldfd共用一个文件表项。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int fd,newfd;
char buf[80];
read(0,buf,80);
write(1,buf,80);
fd=open("./tmp.txt",O_RDONLY);
close(0);
newfd = dup(fd);
if(newfd != 0){
perror("dup");
exit(1);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
return 0;
}
//重定向方法三 : open … dup2 … close
dup2 函数: 功能:复制一个文件描述符
定义:int dup ( int oldfd , int newfd );
参数:oldfd 表示将要复制的文件描述符,newfd 表示复制后得到的新的文件描述符
返回值:-1,出错; >-1,表示新的文件描述符
dup2在复制文件描述符时,如果newfd对应有打开的文件,那么系统会先关闭newfd,然后再复制。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int fd,newfd;
char buf[80];
read(0,buf,80);
write(1,buf,80);
fd=open("./tmp.txt",O_RDONLY);
if(fd != -1){
perror("open");
exit(1);
}
newfd = dup2(fd,0);
if(newfd != 0){
perror("dup2");
exit(1);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
return 0;
}
//重定向方法四: 使用popen实现重定向
popen函数: 功能:建立一个指向进程的流
定义:FILE *popen(char *cmd, char * mode);
参数:cmd 要执行的进程
mode 使用进程的方式
返回值: 非NULL 指向进程的流指针
NULL 失败
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]){
FILE *fp;
char buf[80];
int i=0;
fp=popen("ls -l","r");
while(fgets(buf,80,fp) != NULL){
printf("%s\n",buf);
}
pclose(fp);
return 0;
}
//示例: 实现 ls -l >list.txt
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,int *argv[]){
int pid,fd;
printf("This is show how ro redirect ! \n");
if((pid = fork()) == -1){ //进程创建失败
perror("fork");
exit(1);
}
else if(pid == 0){ //子进程
close(1);
fd = creat("list.txt",0644);
if(execlp("ls","ls -l",NULL) < 0){
perror("exec");
exit(1);
}
}
else{ //父进程
if(pid != 0){
wait(NULL);
system("cat list.txt");
}
}
return 0;
}
二. 管道命令
1 . 管道 cat /etc/passwd|grep root,类似于编写程序中的函数调用 grep(root, cat(“/etc/passwd”));
管道命令将cat命令的输出作为grep命令的输入
管道命令可以更复杂: cat /etc/passwd|grep root|more
也可以和重定向一起使用 :cat /etc/passwd|grep root>result.txt
2. 管道的基本特点:
管道是特殊类型的文件,在满足先入先出的原则下可能进行读写,但不能定位读写位置
管道是单向的,要实现双向通信,需要使用两个管道
匿名管道可以实现具有亲缘关系的进程之间通信,命名管道可以实现在本机任意两个进程之间通信
3. 使用管道的注意事项:
(1)执行读操作时
如果管道中无数据,则读进程将被挂起直到数据被写进管道
如果所有写进程都关闭了管道的写端时,read返回0,意味着文件的结束。
当有多个进程对管道做读操作,需要有某种方法来协调这些读进程对管道的访问。
(2)执行写操作时
当管道已满时,写进程再对管道做写操作时,写进程会被阻塞
如果所有读进程关闭了管道的读端,再执行写操作时,写进程将会收到SIGPIPE信号,
若该信号不 能终止进程,则write调用返回-1,并将errno置为EPIPE
写入管道的数据大小,POSIX规定内核不会拆分小于512字节的块,
因此如果有两个进程向管道写数据,只要每一个进程都限制消息不大于512字节,
写入的消息就不会被内核拆分。
(3)管道分为匿名管道和命名管道
4. 匿名管道:其内部实现隐藏在内核中,实质是一个以队列方式读写的内核缓冲区。
匿名管道可以实现具有亲缘关系的进程之间的通信
(1)创建匿名管道:
pipe函数: 功能:创建一条匿名管道
定义:int pipe ( int parr[2] );
参数:parr 存放管道两端文件描述符的数组,parr[0]中存放管道的读端,parr[1]中存放的是管道的写端
返回值:-1,出错; 0 ,成功
(2)使用示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char *argv[]){
int pfd[2];
char buf[80];
if(pipe(pfd) == -1){
perror("pipe");
exit(1);
}
printf("The pipe wil read from : %d , write to %d.\n",pfd[0],pfd[1]);
write(pfd[1],"This is write to pipe!\n",23);
read(pfd[0],buf,23);
printf("%s",buf);
return 0;
}
5. 命名管道: 无亲缘关系的进程间通信可以选择命名管道
(1)创建命名管道:
mkfifio函数:功能:创建一条命名管道
定义:int mkfifo(char *filename, mode_t mode);
参数:filename 创建的FIFO文件名
mode 文件的权限模式
返回值:-1,出错; 0 ,成功
(2)使用示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int pid,fd;
char buf[80];
mkfifo("fifotest",0644);
if((pid = fork()) > 0){
fd=open("fifotest",O_WRONLY);
write(fd,"Message to test FIFO!",22);
close(fd);
exit(0);
}
else if( pid == 0){
fd=open("fifotest",O_RDONLY);
read(0,buf,80);
printf("%s\n",buf);
close(fd);
exit(0);
}
else{#
perror("fork");
exit(1);
}
return 0;
}