Redirection at the end of the pipe (C shell)

僤鯓⒐⒋嵵緔 提交于 2020-01-02 10:26:07

问题


I'm trying to make ls | tr a b > text.txt I have piping done, but I can't add STDOUT to the end of the pipe (STDOUT in my case can be only in the last argument)

I mark the part of the code, in which redirection should be done, I think that file should be opened, and dup2 method used, but I don't know in which way

Methods contains piping -

enum reqType { PIPE, STDOUT };

int spawn_proc (int in, int out, char** cmd) {
    pid_t pid;

    if ((pid = fork ()) == 0) {
        if (in != 0) {
          dup2 (in, 0);
          close (in);
        }
        if (out != 1) {
          dup2 (out, 1);
          close (out);
        }

      return execvp (cmd[0], cmd);
    }

  return pid;
}

void fork_pipes (int n, char** cmd[], enum reqType type) {
  int i;
  pid_t pid;
  int in, fd [2];

  in = 0;

  for (i = 0; i < n - 1; ++i) {
      if(type == PIPE || i < n-2) {
        pipe (fd);

        spawn_proc (in, fd [1], cmd[i]);

        close (fd [1]);

        in = fd [0];
      }
      else if(type == STDOUT && i == n-2) {
            ///HOW TO IMPLEMENT THIS PART?
      }
    }

  if (in != 0)
    dup2 (in, 0);

  execvp (cmd[i][0], cmd[i]);
}

EDIT in the marked by /// place I wrote

pipe(fd);
int out = open(cmd[n-1][0],O_WRONLY|O_CREAT|O_TRUNC);
spawn_proc(in, out, cmd[i]);
close(fd[1]);

回答1:


I think that file should be opened, and dup2 method used, but I don't know in which way

You are right about the mechanisms for implementing the redirection. It should be done on the process intended for tr, and before performing the overlay.


Let's go step by step:

ls | tr a b > text.txt

First create a pipe, then fork().

From now on, there are two processes running in parallel, both of them will be eventually overlaid by means of exec(): one with the ls program, the other with the tr program.

Process for ls:

  1. Close the reading end of the pipe: this process will only write to the pipe.
  2. dup2() the writing end of the pipe to STDOUT: what this process writes to STDOUT is being written to the pipe.
  3. Perform the overlay: exec() with ls.

Process for tr:

  1. Close the writing end of the pipe: this process will only read from the pipe.
  2. dup2() the reading end of the pipe to STDIN: what this process reads from STDIN is coming from the pipe.
  3. In order to perform the redirection to the text.txt file, first open() the file text.txt for writing and with the flags O_CREAT and O_TRUNC, then dup2() the obtained file descriptor to STDOUT.

  4. Perform the overlay: exec() with tr.


Note that, if the command were appending to text.txt instead of truncating it (i.e.: using >> instead of >):

ls | tr a b >> text.txt

You would have to use the flag O_APPEND instead of O_TRUNC when open()ing the text.txt file.


Code Snippet

I've modified your code (also the interface of fork_pipes()). It's a minimal example that runs, I hope it helps.

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int spawn_proc (int in, int out, char** cmd) {
    pid_t pid;

    if ((pid = fork ()) == 0) {
        if (in != 0) {
          dup2 (in, 0);
          close (in);
        }
        if (out != 1) {
          dup2 (out, 1);
          close (out);
        }

      return execvp (cmd[0], cmd);
    }

    return pid;
}

void fork_pipes (char** cmd[], const char *redirection) {
  int i, n;
  int in, out, fd[2];

  in = 0;

  // obtain n from the NULL terminated cmd array
  for (n = 0; cmd[n]; ++n)
    ;

  // process all but the last elemet of the pipe
    for (i = 0; i < n-1; ++i) {
        pipe(fd);
        spawn_proc(in, fd[1], cmd[i]);
        close(fd [1]);
        in = fd [0];
  }

  // process the last element of the pipe
    if (redirection) {
        out = open(redirection, O_WRONLY | O_CREAT | O_TRUNC);
        fchmod(out, 0666);
    } else
        out = STDOUT_FILENO;

    if (in != 0)
        dup2(in, 0);

    spawn_proc(in, out, cmd[i]);
}

int main()
{

    char *cmd1[] = {"ls", NULL};
    char *cmd2[] = {"tr", "a", "b", NULL};
    char **cmd[] = { cmd1, cmd2, NULL};

    // redirected to text.txt
    fork_pipes(cmd, "text.txt");

    // no redirection
    fork_pipes(cmd, NULL);

    // another example with a longer pipe 
    {
        char *cmd1[] = {"echo", "hello world", NULL};
        char *cmd2[] = {"tee", NULL};
        char *cmd3[] = {"tee", NULL};
        char *cmd4[] = {"tr", "lo", "10", NULL};

        char **cmd[] = {cmd1, cmd2, cmd3, cmd4, NULL};

        // redirected to redirection.txt
        fork_pipes(cmd, "redirection.txt");

        // no redirected
        fork_pipes(cmd, NULL);
    }

    return 0;
}

As already pointed out in this comment. You just need to call pipe() once in your example: The pipe() system call only needs to be called once for each pipe operator (i.e.: the | character) found in the compound command. For example, in the following command:

cmd1 | cmd2 | cmd3 | cmd4

pipe() must be called exactly four times, since there are four pipe operators.



来源:https://stackoverflow.com/questions/47478392/redirection-at-the-end-of-the-pipe-c-shell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!