【问题标题】:Redirection at the end of the pipe (C shell)管道末端的重定向(C shell)
【发布时间】:2017-11-24 18:21:25
【问题描述】:

我正在尝试制作ls | tr a b > text.txt 我已经完成了管道,但我无法将 STDOUT 添加到管道的末尾(在我的情况下,STDOUT 只能在最后一个参数中)

我标记了代码中应该做重定向的部分,我认为应该打开那个文件,并使用dup2方法,但我不知道以哪种方式

方法包含管道 -

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]);
}

编辑 在标记为 /// 我写的地方

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

【问题讨论】:

  • 使用open 获取重定向文件的文件描述符并通过管道传输到该文件。我会在传递命令之前处理重定向,您可能需要检查 shell 上 strace 的输出,看看它是如何处理这个的
  • 请看问题的编辑部分,那里有一些地方需要改变
  • 我认为spawn_proc(in, out, cmd[i])中的in应该改为fd[0],即:管道的读取端
  • 但是在之前的交互中有in = fd[0]所以这完全一样
  • 您不需要管道进行重定向,它应该从先前的管道读取端读取。只需将文件描述符从open 复制到标准输出即可。您可能希望为您的公开通话添加权限,例如。 0666.

标签: c shell unix pipe io-redirection


【解决方案1】:

我认为应该打开该文件,并使用dup2方法,但我不知道以哪种方式

您对实现重定向的机制是正确的。它应该在用于tr 的进程上完成,并且在执行覆盖之前完成。


让我们一步一步来:

ls | tr a b > text.txt

先创建一个管道,然后fork()

从现在开始,有两个进程并行运行,它们最终都会通过exec()重叠:一个使用ls 程序,另一个使用tr 程序。

ls 的处理过程:

  1. 关闭管道的读取端:这个进程只会写入管道。
  2. dup2()STDOUT 的管道写入端:此进程写入STDOUT 的内容正在写入管道。
  3. 执行覆盖:exec()ls

tr 的进程:

  1. 关闭管道的写端:这个进程只会从管道中读取。
  2. dup2() 管道的读取端STDIN:此进程从STDIN 读取的内容来自管道。
  3. 为了执行重定向text.txt文件,首先open()文件text.txt用于写入并带有标志O_CREATO_TRUNC,然后dup2()获取到的文件描述符为STDOUT

  4. 执行覆盖:exec()tr


请注意,如果命令附加到 text.txt 而不是截断它(即:使用 &gt;&gt; 而不是 &gt;):

ls | tr a b >> text.txt

open() 处理text.txt 文件时,您必须使用标志O_APPEND 而不是O_TRUNC


代码片段

我修改了你的代码(也是fork_pipes()的接口)。这是一个运行的最小示例,希望对您有所帮助。

#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;
}

正如this comment 中已经指出的那样。您只需要在您的示例中调用一次pipe()pipe() 系统调用只需要为每个 管道运算符调用一次一次(即:|字符)在复合命令中找到。例如,在以下命令中:

cmd1 | cmd2 | cmd3 | cmd4

pipe() 必须准确调用四次,因为有四个 管道运算符

【讨论】:

  • 我尝试按照我附加到问题的方式关注评论,但它不起作用
猜你喜欢
  • 2016-06-03
  • 2023-03-04
  • 2013-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
相关资源
最近更新 更多