Linux C 重定向和管道 学习

一.  重定向命令

1.  在shell中运行命令时,系统为每个进程自动打开三个文件描述符。

输入重定向 :more</etc/passwd         输出重定向: 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;
}

                              

转载于:https://my.oschina.net/xieganying/blog/785332

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值