Linux C 重定向和管道 学习

一. 重定向命令

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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英子的搬砖日志

您的鼓励是我创作的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值