execv *并写入stdin

时间:2011-11-11 11:36:47

标签: c unix pipe

我正在尝试运行具有特定标准输入的程序。我成功使用了一个文件的文件描述符,其中有我想要放入stdin的内容,但是我无法直接在stdin上编写:

$cat input.test
echo Hello
$

代码C:

int main(int argc, char **argv)
{
  int fd = 0;

  fd = open("input.test", O_CREAT);
  close(STDIN_FILENO);
  dup2(fd, STDIN_FILENO);
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
}

有效:

$./a.out
Hello
$

但是,如果我尝试使用管道直接在STDIN上编写,程序将不显示任何内容并继续运行:

int main(int argc, char **argv)
{
  int fds[2];

  pipe(fds);
  close(STDIN_FILENO);
  dup2(fds[1], STDIN_FILENO);
  write(fds[1], "echo Hello;", 11); // Résults are identics with fds[0]
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
}

感谢您的帮助

亲切, 巴斯蒂安。

编辑问题已解决:

感谢您的回答,这里的代码有效:

int main(void)
{
    int fd[2];
    pid_t pid;

    if (pipe(fd) < 0)
        return EXIT_FAILURE;

    if ((pid = fork()) < 0)
        return EXIT_FAILURE;
    else if (pid != 0) { /* father */
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        execlp("bash", "bash", (char *)0);
    } else { /* son */
        close(fd[0]);
        write(fd[1], "echo hello\n", 11);
    }

    return EXIT_SUCCESS;
}

4 个答案:

答案 0 :(得分:4)

您需要将管道的读取端复制到stdin,而不是写入端。 (显然,写信给写方面。)

#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
  int fds[2];
  char cmd[] = "echo hello\nexit\n";

  pipe(fds);
  close(STDIN_FILENO);
  dup2(fds[0], STDIN_FILENO);
  write(fds[1], cmd, strlen(cmd));
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
  return 0;
}

请确保检查所有这些功能的返回值,但如果不这样做,您将永远无法调试代码。

答案 1 :(得分:3)

execv和朋友将当前正在运行的程序替换为指定的程序;他们不回来 - 在新计划开始时继续执行。

所以你通常做的是fork,并且在其中一个分支中,拨打execv。然后,您继续在另一个fork中读取和写入程序中的管道。在大多数语言中,通常有popen个函数可以执行此操作;遗憾的是,在POSIX中,popen()是严格读取或写入的,而不是双向的。

幸运的是,我已经 made, tested and published popen3功能。这将为您提供三个文件描述符 - 一个用于stdin到进程,两个用于stdout和stderr。然后,您可以在stdin上使用write()。

答案 2 :(得分:2)

调用管道时,fd [0]打开以进行读取,fd [1]打开以进行写入。你应该在读取端(fd [0])复制stdin并写入写入端(fd [1])。检查write的返回值:它可能是-1。

但是有一个更大的问题。你永远不会关闭管道的任何一侧。 bash可能会阻塞读取,并且在管道的写入侧关闭之前不会执行任何操作。在复制和写入后,应关闭管道的两侧。 (或设置FD_CLOEXEC)。

答案 3 :(得分:0)

另请注意,按照您的方式执行操作,您将依赖于管道缓冲区大小。如果写得太多,写入将被阻止,因为没有读者。做得可靠,你应该fork(),在子进程中执行exec并写入父进程中的管道。通过这种方式,管道将有一个阅读器,您可以根据需要编写尽可能多的数据。