C语言 将 exec 输出重定向到缓冲区或文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2605130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:07:34  来源:igfitidea点击:

Redirecting exec output to a buffer or file

cexecfork

提问by devin

I'm writing a C program where I fork(), exec(), and wait(). I'd like to take the output of the program I exec'ed to write it to file or buffer.

我正在写一个C程序中我fork()exec()wait()。我想将我执行的程序的输出写入文件或缓冲区。

For example, if I exec lsI want to write file1 file2 etcto buffer/file. I don't think there is a way to read stdout, so does that mean I have to use a pipe? Is there a general procedure here that I haven't been able to find?

例如,如果我 execls我想写入file1 file2 etc缓冲区/文件。我认为没有办法读取标准输出,所以这是否意味着我必须使用管道?这里有没有我找不到的一般程序?

回答by R Samuel Klatchko

For sending the output to another file (I'm leaving out error checking to focus on the important details):

将输出发送到另一个文件(我省略了错误检查以专注于重要细节):

if (fork() == 0)
{
    // child
    int fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    dup2(fd, 1);   // make stdout go to file
    dup2(fd, 2);   // make stderr go to file - you may choose to not do this
                   // or perhaps send stderr to another file

    close(fd);     // fd no longer needed - the dup'ed handles are sufficient

    exec(...);
}

For sending the output to a pipe so you can then read the output into a buffer:

用于将输出发送到管道,以便您可以将输出读入缓冲区:

int pipefd[2];
pipe(pipefd);

if (fork() == 0)
{
    close(pipefd[0]);    // close reading end in the child

    dup2(pipefd[1], 1);  // send stdout to the pipe
    dup2(pipefd[1], 2);  // send stderr to the pipe

    close(pipefd[1]);    // this descriptor is no longer needed

    exec(...);
}
else
{
    // parent

    char buffer[1024];

    close(pipefd[1]);  // close the write end of the pipe in the parent

    while (read(pipefd[0], buffer, sizeof(buffer)) != 0)
    {
    }
}

回答by Jonathan Leffler

You need to decide exactly what you want to do - and preferably explain it a bit more clearly.

你需要确切地决定你想要做什么 - 最好更清楚地解释一下。

Option 1: File

选项 1:文件

If you know which file you want the output of the executed command to go to, then:

如果您知道要执行命令的输出到哪个文件,则:

  1. Ensure that the parent and child agree on the name (parent decides name before forking).
  2. Parent forks - you have two processes.
  3. Child reorganizes things so that file descriptor 1 (standard output) goes to the file.
  4. Usually, you can leave standard error alone; you might redirect standard input from /dev/null.
  5. Child then execs relevant command; said command runs and any standard output goes to the file (this is the basic shell I/O redirection).
  6. Executed process then terminates.
  7. Meanwhile, the parent process can adopt one of two main strategies:
    • Open the file for reading, and keep reading until it reaches an EOF. It then needs to double check whether the child died (so there won't be any more data to read), or hang around waiting for more input from the child.
    • Wait for the child to die and then open the file for reading.
    • The advantage of the first is that the parent can do some of its work while the child is also running; the advantage of the second is that you don't have to diddle with the I/O system (repeatedly reading past EOF).
  1. 确保父母和孩子就名字达成一致(父母在分叉之前决定名字)。
  2. 父分叉 - 您有两个进程。
  3. Child 重新组织内容,以便文件描述符 1(标准输出)进入该文件。
  4. 通常,您可以不理会标准错误;您可能会从 /dev/null 重定向标准输入。
  5. 孩子然后执行相关命令;所述命令运行并且任何标准输出都进入文件(这是基本的 shell I/O 重定向)。
  6. 执行的进程然后终止。
  7. 同时,父进程可以采用以下两种主要策略之一:
    • 打开文件进行读取,并继续读取直到到达 EOF。然后它需要仔细检查孩子是否死了(所以不会有更多的数据要读取),或者等待孩子的更多输入。
    • 等待孩子死亡,然后打开文件阅读。
    • 第一个的好处是父进程可以在子进程运行的同时完成它的一些工作;第二个的优点是你不必与 I/O 系统(反复阅读过去的 EOF)混为一谈。

Option 2: Pipe

选项 2:管道

If you want the parent to read the output from the child, arrange for the child to pipe its output back to the parent.

如果您希望父级读取子级的输出,请安排子级将其输出通过管道返回给父级。

  1. Use popen() to do this the easy way. It will run the process and send the output to your parent process. Note that the parent must be active while the child is generating the output since pipes have a small buffer size (often 4-5 KB) and if the child generates more data than that while the parent is not reading, the child will block until the parent reads. If the parent is waiting for the child to die, you have a deadlock.
  2. Use pipe() etc to do this the hard way. Parent calls pipe(), then forks. The child sorts out the plumbing so that the write end of the pipe is its standard output, and ensures that all other file descriptors relating to the pipe are closed. This might well use the dup2() system call. It then executes the required process, which sends its standard output down the pipe.
  3. Meanwhile, the parent also closes the unwanted ends of the pipe, and then starts reading. When it gets EOF on the pipe, it knows the child has finished and closed the pipe; it can close its end of the pipe too.
  1. 使用 popen() 以简单的方式执行此操作。它将运行该进程并将输出发送到您的父进程。请注意,当子进程生成输出时,父进程必须处于活动状态,因为管道的缓冲区大小很小(通常为 4-5 KB),如果子进程生成的数据多于父进程未读取时的数据,子进程将阻塞,直到家长读。如果父母正在等待孩子死亡,那么您就会陷入僵局。
  2. 使用 pipe() 等方法很难做到这一点。父调用 pipe(),然后分叉。子进程整理管道,使管道的写端成为其标准输出,并确保与管道相关的所有其他文件描述符都已关闭。这很可能使用 dup2() 系统调用。然后它执行所需的进程,该进程将其标准输出发送到管道中。
  3. 同时,父级也会关闭管道的不需要的末端,然后开始读取。当它在管道上得到 EOF 时,它知道孩子已经完成并关闭管道;它也可以关闭管道的末端。

回答by Blindy

Since you look like you're going to be using this in a linux/cygwin environment, you want to use popen. It's like opening a file, only you'll get the executing programs stdout, so you can use your normal fscanf, freadetc.

由于您看起来要在 linux/cygwin 环境中使用它,因此您想使用popen。这就像打开一个文件,只有你会得到执行的程序stdout,这样你就可以使用正常的fscanffread等等。

回答by Ignacio Vazquez-Abrams

After forking, use dup2(2)to duplicate the file's FD into stdout's FD, then exec.

fork后,使用dup2(2)将文件的FD复制到stdout的FD中,然后执行。

回答by Jasper Koning

You could also use the linux shcommand and pass it a command that includes the redirection:

您还可以使用 linuxsh命令并向其传递一个包含重定向的命令:

string cmd = "/bin/ls > " + filepath;

execl("/bin/sh", "sh", "-c", cmd.c_str(), 0);