SHELL重定向和管道的实现

原文链接

I have been always fascinated about the design of UNIX. I am still curious and enjoy the philosophy and the idea of ‘Write programs that do one thing and do it well’. Aim of this blog post is to walk through some interesting aspects on implementation of file descriptors and to illustrate how gracefully that design helps to build interesting unix shell methodologies. For any process, there are three default file descriptors(文件描述符). Stdin - with descriptor number 0, Stdout - with descriptor number 1 and Stderr with descriptor number 2.

Let us go through some basic system calls. All the system calls explained below are not exact syntax. Please refer man for correct function prototype.

Basic System Calls (基础系统调用)

  1. fork()
    The fork system call creates another copy of current process and mark the new process as child of parent process which called fork. This system call returns zero in the child process and return child’s pid in the parent process. It copies everything including file descriptors, and virtual memory. If a process tries to write any virtual memory page, it will do a copy on write to create copy of that particular page for that process space.
  2. exec(binary_path)
    The exec system call overwrites the current process with executable image from a file. Eg. if you run exec(“/bin/ls”). It will overwrite the memory code image with binary from /bin/ls and execute. The file descriptor table remains the same as that of original process.
  3. open(file, mode)
    Opens a file and creates a file descriptor associated with the file.

IMPORTANT: By design, when the kernel allocates a file descriptor, it will create the fd with next smallest available file descriptor number.

  1. close(fd)
    Closes the open file descriptor
  2. dup(fd)
    The dup system call creates a file descriptor that is duplicate of given fd passed as argument.
  3. pipe(int arr[2])
    Creates a pipe, and stores the read descriptor in array location zero and write descriptor in array location one.
  4. read(fd, buff, len)
    Reads len bytes to buff from file descriptor fd.
  5. write(fd, buff, len)
    Writes len bytes from buff to file descriptor fd.

Let us go through some interesting shell features that we use frequently and look at their implementations.

Redirections (重定向)

$ cmd1 > stdout.txt

The above command redirects stdout to file stdout.txt

For implementing the above operation, we should be able to link stdout of cmd1 with file descriptor of stdout.txt opened with write mode. Let us look at the code.

main(){
    close(1); // Release fd no - 1
    open("stdout.txt", "w"); // Open a file with fd no = 1
    // Child process
    if (fork() == 0) {
        exec("cmd1"); // By default, the program writes to stdout (fd no - 1). ie, in this case, the file
    }
}

$ cmd1 2> stdout.txt

The above command redirects stderr to file stdout.txt

main(){
    close(2); // Release fd no - 2
    open("stderr.txt", "w"); // Opens file with fd no - 2
    // Child process
    if (fork() == 0) {
        exec("cmd1"); // Writes to stderr (fd no 2)
    }
}

$ cmd2 > stdout_stderr.txt 2>&1

The above command redirects both stdout and stderr to file stdout_stderr.txt

main(){
    close(1); // Release fd no - 1
    open("stdout_stderr.txt", "w"); //Opens file with fd no - 1
    // Child process
    if (fork() == 0) {
        close(2); // Release fd no - 2
        dup(1); // Create fd no - 2 which is duplicate of fd no -1. Hence, we joined fd 1 and 2 (stdout and  stderr)
        exec("cmd2");
    }
}

$ cmd3 < input.txt

The above command redirects data from input.txt to stdin for cmd3.

main(){
    close(0);//Release fd - 0
    open("stdout.txt", "r"); //Open file with fd - 0

    //Child process
    if (fork() == 0) {
        exec("cmd3"); // By default, program reads from stdin. ie, fd - 0
    }
}

Pipe (管道)

$ cmd1 | cmd2

This command says that cmd2 will receive stdin from stdout of cmd1.

main(){
    int p[2];
    pipe(p); // Creates a pipe with file descriptors Eg. input = 3 and output = 4 (Since, 0,1 and 2 are not available)

    if (fork() == 0) {
    // Child process
        close(0); // Release fd no - 0
        close(p[0]); // Close pipe fds since useful one is duplicated
        close(p[1]);
        dup(p[0]); // Create duplicate of fd - 3 (pipe read end) with fd 0.
        exec("cmd2");
    } else {
        //Parent process
        close(1); // Release fd no - 1
        close(p[0]); // Close pipe fds since useful one is duplicated
        close(p[1]);
        dup(p[1]); // Create duplicate of fd - 4 (pipe write end) with fd 1.
        exec("cmd1");
    }
}

Aren’t you feeling awesome? With simple design, without making any code change to individual programs, it is possible to connect input and output streams to individual programs. Hats off to designers of UNIX.

时间: 2024-10-29 20:38:11

SHELL重定向和管道的实现的相关文章

linux shell 管道命令(pipe)使用及与shell重定向区别_linux shell

看了前面一节:linux shell数据重定向(输入重定向与输出重定向)详细分析 估计还有一些朋友是头晕晕的,好复杂的重定向了.这次我们看下管道命令了.shell管道,可以说用法就简单多了. 管道命令操作符是:"|",它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandard error 信息没有直接处理能力.然后,传递给下一个命令,作为标准的输入 standard input. 管道命令使用说明: 先看下下面图: comma

RHCE 学习笔记(4)- 重定向,管道和VIM编辑器

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://beanxyz.blog.51cto.com/5570417/1592526 这一节主要学习了重定向,管道和VIM编辑器 首先看看输出重定向 1 > 或者> 都表示将正确的信息覆盖输出重定向, 1>>或者>>表示追加的重定向 2 > 表示将错误的信息覆盖输出重定向, 2>>表示追加的错误信息重定向 &> 将所有的信息,正确

Linux shell编程 12 ---- 管道+I/O重定向

 1 管道简介      1 管道技术是Linux的一种基本的进程间通信技术,它利用先进先出排队模型来指挥进程间的通信.对于管道,我们可以形象的理解为连接两个实体的一个单向连接器      2 Shell编程中管道符号是"|",命令直接利用管道进行通信的一般格式         command1 | command2 | command3 | ... | commandn         command1-commandn是n个命令,如果没有管道那么结果将会直接显示在Shell中,当S

5个DOS专用文件的6种io重定向(实现管道原理)

1. 重定向 以下a.asm,用含't'及回车13的txt,仿command.com,依masm命令行的arg符号值N,(如masm /Darg=0 a;),重定向5个DOS开启文件(STD_): N=0:是STDIN句柄,先bin方式读1符,再从txt输't'到charN=1:是STDOUT句柄,输出char的'1'到txtN=2:是STDERR句柄,输出char的'2'到txt N=30,31:3是STDAUX句柄,N=30,从txt输't'到char;N=31,输出char的'3'到txt

linux shell重定向

  将shell的错误输出重定向到标准输出,这样就可以记录脚本的出错信息. 如: /root/test.sh >> test.log 2>&1 注: linux先加载 /etc/rc.d/rc.local 后加载 /etc/profile

重定向和管道的实现

重定向标准输入的实现: 1)close-then-open: close(0) ; fd=open("test",O_RDONLY); fd将是stdin. 2)open-close-dup-close: fd=open(file),打开stdin将要重定向的文件:close(0);new_fd=dup(file);close(fd);new_fd就是被重定向的stdin 3)open-dup2-close: fd=open(file);new_fd=dup2(fd,0);close(

shell pipe-vmstat n 管道至 curl

问题描述 vmstat n 管道至 curl 使用 vmstat n命令每间隔n秒不间断的输出系统状态.而目前想把每次输出状态立即通过curl POST到一个url上.是否可以通过管道完成? 解决方案 You can use the -d option in curl with a @- argument to accept input from a pipe. http://serverfault.com/questions/313599/how-do-i-pipe-the-output-of

shell中的管道命令及并行执行

发现在写脚本的的时候有三个符号都很有用一个是管道命令符"|",一个是并行执行命令符"&",另一个是"&&" 所谓管道,充当的作用就是连接管道的前后两个部分.具体来说就是将管道前的命令执行的结果作为管道后的输入.例如android中查看dns相关属性的时候就可以是用命令getprop |grep dns由于getprop返回的结果较多,但是并不是所有的都是我们目前所关心的,使用grep命令对getprop的输出进行过滤,只保

linux数据流重定向和管道

1.标准输入.输出.错误 在执行一个指令的时候,这个指令可能会由文件读入资料,然后经过处理,再将数据输出到屏幕上.一般来说,要执行一个指令,其流程是这样的: 1.标准输入(stdin):代码为0,使用< 或 <<  2. 标准输出(stdout):代码为1,使用>或>> 3.标准错误输出(stderr):代码为2,使用2>或2>> 例如,我们想把/目录下的所有文件用:ls -l 命令列出,但是不显示在桌面,而是显示在一个新建的文件里,我们可以执行如下命