当前位置:   article > 正文

linux -- 管道和重定向_linux 重定向scanf 管道

linux 重定向scanf 管道

一、管道和重定向命令

1.1、管道命令 |

  • 前一个命令的输出作为后一个命令的输入
  • 可以叠加
 $ ps -u
 $ ps -u | grep pts
 $ ps -u | grep pts | grep pts/0
  • 1
  • 2
  • 3

1.2、重定向命令 < , > , >>

  • 输入: <

  • 输出: >

  • 追加的方式输入: >>

    #include <stdio.h>
    int main(int argc,char *argv[]) {
        //从标准输入中获取
        char str[1024];
        scanf("%s",str);
        //往标准输出中打印argc argv
        printf("argc = %d\n",argc);
       for(int i = 0;i<argc;i++){
           fprintf( stdout, "%s\n", argv[i]);
       }
        printf("%s\n",str);
       //往标准错误中打印
        fprintf(stderr,"this is stderr\n");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    #不重定向
    $ ./test  
    #重定向 执行后不会打印出来,不用再输入参数
    $ ./test01 0<in.txt 1>out.txt 2>err.txt
     #以追加的方式重定向,不会清空原文件
    $ ./test01 2>>err.txt 
    #要将文本输入到标准输入 只能用 0<xxx.txt
    :<<annotation
    	标准输入 stdin 0
    	标准输出 stdout 1
    	标准错误 stderr 2
    annotation
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 结论

    重定向由shell实现
    重定向符号和重定向文件不是参数

二、重定向编程

2.1、close then open

1、先关闭标准输出的文件描述符
2、再打开一个文件
3、这个文件的文件描述符就变成了1,从而实现重定向功能

  • 每次获取文件描述符,系统总会分配最小且可用的描述符
  • 适用于有文件名的情况
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc,char *argv[]) {
    //输出到标准输出
    char buf[]  = "1234567890\n";
    write(1,buf, sizeof(buf) - 1 );//sizeof会计算  \0

    //关闭标准输出,重定向到指定文件
    close(1);
    int fd = open("./out.txt",O_CREAT | O_RDWR | O_TRUNC,666);
    if(fd < 0){
    	perror("open");
    	exit(-1);
    }
    write(1,buf, sizeof(buf) - 1 );
    close(fd);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

2.2、dup函数

#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);
  • 1
  • 2
  • 3
  • 4
  • 功能

    复制一个文件描述符

  • 参数说明
    oldfd 要复制的文件描述符
    newfd 复制后得到的文件描述符

  • 返回值
    -1 出错
    > -1 新的文件描述符

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

int main(int argc,char *argv[]) {
    int fd = open("./out.txt",O_CREAT | O_RDWR | O_TRUNC,666);
    if(fd<0){
        perror("open");
        exit(-1);
    }
    //输出到标准输出
    char buf[]  = "hello\n";
    write(1,buf, sizeof(buf) - 1 );//sizeof会计算  \0

	//关闭标准输出,重定向到指定fd 方法1
   close(1);
    int newfd = dup(fd);
    if(newfd != 1){
        perror("dup");
        exit(-1);
    }
    close(fd);
    write(1,buf, sizeof(buf) - 1 );
    close(newfd);
    
	//关闭标准输出,重定向到指定fd 方法2
//    dup2(fd,1);
//    if(fd != 1){
//        perror("dup2");
//        exit(-1);
//    }
//    write(1,buf, sizeof(buf) - 1 );
//    close(fd);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

在这里插入图片描述

  • 补充
    1、 如果只能拿到要重定向的文件的文件描述符,可用此方法
    2、dup会申请一个新的文件描述符,同样是在最小可用当中去找
    3、新的文件描述符和旧的一样,指向同样的文件
    4、 dup2(int oldfd, int newfd) 如果newfd存在,则会先被关闭

三、管道编程

3.1、匿名管道(单工)

 #include <unistd.h>
 
 int pipe(int pfd[2]); 
 //man 手册是 pipefd,我简写成 pfd;
  • 1
  • 2
  • 3
  • 4
  • 参数说明
    pfd 存放管道两端文件描述符的数组
    pfd[0] 存放读端的描述符
    pfd[1]存放写端的描述符
  • 返回值
    -1 错误
    0 成功
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>
#include <string.h>

int main() {
    int pfd[2];
    pid_t pid;
    char buf[] ="1234567890";
    if(pipe(pfd) == -1){
        perror("pipe");
        exit(-1);
    }

    pid = fork();
    if(pid<0){
        perror("fork");
        exit(-1);
    }
    else if(pid == 0){

        close(pfd[1]);
        memset(buf,0, sizeof(buf));
        read(pfd[0],buf, sizeof(buf) - 1 );
        printf("子进程接收 :  %s\n",buf);
        close(pfd[0]);

        exit(0);
    }
    else{

        close(pfd[0]);
        write(pfd[1], buf, sizeof(buf) - 1);
        printf("父进程发送 : %s \n",buf);
        close(pfd[1]);

        wait(NULL);
        exit(0);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在这里插入图片描述

  • 补充
  1. pipe创建的是匿名管道,实质是一个队列 写时入队,读时出队
  2. 一般创建管道再创建子进程
  3. 匿名管道只能单向通信来保证准确性
  4. 只有有亲缘关系的进程才能用匿名管道通信
  5. 进程操作
    若管道中无数据,则会被阻塞,直到有数据写入
    所有进程都关闭了写端,此时read就会返回0
  6. 进程操作
    若管道已满,则会被阻塞
    如果所有进程关闭了读端,此时写操作会出现段错误

3.2、命名管道

  • 创建管道文件命令
#创建一个名为 fifo 的命名管道文件
$ mknod fifo p 
  • 1
  • 2
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
  • 1
  • 2
  • 3
  • 4
  • 功能
    创建命名管道文件
  • 参数
    pathname 要创建的文件名
    mode 文件权限
  • 返回值
    0 成功
    -1 失败
    mkfifo("fifo",0644);
    int fd = open("fifo",O_RDWR);
  • 1
  • 2
  • 补充
    1、命名管道可以实现任意两个进程间的通信
    2、管道是特殊类型的文件,实质也是队列,不能用lseek函数
    3、读时若管道为空也会被阻塞,

3.3、popen

#include <stdio.h>

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
  • 1
  • 2
  • 3
  • 4
  • 功能
    通过创建管道、fork和调用shell来打开进程
  • 参数
    command:要执行的命令或程序 (实际是打开了子进程)
    type:分为 ‘r’和‘w’
    r 表示读取command 的输出
    w 表示写到command 的输入
  • 先写个hello
#include <stdio.h>

int main() {
    char buf[12] = {'\0'};
    fgets(buf, sizeof(buf),stdin);
    printf("%s\n",buf);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • test程序
#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main() {
    FILE *fp = popen("./hello","w");
    int fd = fileno(fp);
    char buf[] = "hello world";
    write(fd,buf, sizeof(buf) - 1);
    wait(NULL);
    close(fd);
    pclose(fp);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 执行test
    在这里插入图片描述

四、综合应用

  • shell 命令
$ ls -l / | grep lib
  • 1
  • 代码实现
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
//为了方便,没写出错处理
int main() {
    //fork一个子进程执行 ls -l / 并拿到输出结果
    FILE *fp = popen("ls -l /","r");
    int fd = fileno(fp);

    //重定向到标准输入
    dup2(fd,0);
    wait(NULL);
    close(fd);
    pclose(fp);
    char* argv[] = {"grep","lib",NULL};
    execvp("grep",argv);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

笔记来源:mooc

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/211935
推荐阅读
相关标签
  

闽ICP备14008679号