使用 libuv 捕获子进程的标准输出
Posted
技术标签:
【中文标题】使用 libuv 捕获子进程的标准输出【英文标题】:Capture a child process's stdout with libuv 【发布时间】:2013-01-22 23:24:38 【问题描述】:我正在使用 libuv。我已经阅读了http://nikhilm.github.com/uvbook/processes.html,但仍然无法弄清楚如何捕获子进程的标准输出,以便它在父进程中可用(但不能代替父进程的标准输入)。
我的代码目前是:
#include <stdio.h>
#include <stdlib.h>
#include "../../libuv/include/uv.h"
uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
uv_pipe_t apipe;
void on_child_exit(uv_process_t *req, int exit_status, int term_signal)
fprintf(stderr, "Process exited with status %d, signal %d\n", exit_status, term_signal);
uv_close((uv_handle_t*) req, NULL);
uv_buf_t alloc_buffer(uv_handle_t *handle, size_t len)
printf("alloc_buffer called\n");
uv_buf_t buf;
buf.base = malloc(len);
buf.len = len;
return buf;
void read_apipe(uv_stream_t* stream, ssize_t nread, uv_buf_t buf)
printf("read %li bytes from the child process\n", nread);
int main(int argc, char *argv[])
printf("spawn_test\n");
loop = uv_default_loop();
char* args[3];
args[0] = "dummy";
args[1] = NULL;
args[2] = NULL;
uv_pipe_init(loop, &apipe, 0);
uv_pipe_open(&apipe, 0);
options.stdio_count = 3;
uv_stdio_container_t child_stdio[3];
child_stdio[0].flags = UV_IGNORE;
child_stdio[1].flags = UV_INHERIT_STREAM;
child_stdio[1].data.stream = (uv_stream_t *) &apipe;
child_stdio[2].flags = UV_IGNORE;
options.stdio = child_stdio;
options.exit_cb = on_child_exit;
options.file = args[0];
options.args = args;
uv_read_start((uv_stream_t*)&apipe, alloc_buffer, read_apipe);
if (uv_spawn(loop, &child_req, options))
fprintf(stderr, "%s\n", uv_strerror(uv_last_error(loop)));
return 1;
return uv_run(loop, UV_RUN_DEFAULT);
dummy.c:
#include <unistd.h>
#include <stdio.h>
int main()
printf("child starting\n");
sleep(1);
printf("child running\n");
sleep(2);
printf("child ending\n");
return 0;
我有一种唠叨的感觉,我还不太明白 libuv 管道的意义。
【问题讨论】:
【参考方案1】:我找到了解决办法:
-
我打错了标志,它们应该是
UV_CREATE_PIPE | UV_READABLE_PIPE
而不是 UV_INHERIT_STREAM
。
我需要在uv_spawn
之后致电uv_read_start
。我假设没有数据丢失的可能性,因为 uv_run 还没有被调用。
以上两个修复显示了从dummy
一次到达的所有输出,而不是三个块(就像它在命令行上所做的那样)。 dummy.c
中的 fflush
修复了此问题。
spawn_test:
#include <stdio.h>
#include <stdlib.h>
#include "../../libuv/include/uv.h"
uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
uv_pipe_t apipe;
void on_child_exit(uv_process_t *req, int exit_status, int term_signal)
fprintf(stderr, "Process exited with status %d, signal %d\n", exit_status, term_signal);
uv_close((uv_handle_t*) req, NULL);
uv_buf_t alloc_buffer(uv_handle_t *handle, size_t len)
printf("alloc_buffer called, requesting a %lu byte buffer\n");
uv_buf_t buf;
buf.base = malloc(len);
buf.len = len;
return buf;
void read_apipe(uv_stream_t* stream, ssize_t nread, uv_buf_t buf)
printf("read %li bytes in a %lu byte buffer\n", nread, buf.len);
if (nread + 1 > buf.len) return;
buf.base[nread] = '\0'; // turn it into a cstring
printf("read: |%s|", buf.base);
int main(int argc, char *argv[])
printf("spawn_test\n");
loop = uv_default_loop();
char* args[3];
args[0] = "dummy";
args[1] = NULL;
args[2] = NULL;
uv_pipe_init(loop, &apipe, 0);
uv_pipe_open(&apipe, 0);
options.stdio_count = 3;
uv_stdio_container_t child_stdio[3];
child_stdio[0].flags = UV_IGNORE;
child_stdio[1].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
child_stdio[1].data.stream = (uv_stream_t *) &apipe;
child_stdio[2].flags = UV_IGNORE;
options.stdio = child_stdio;
options.exit_cb = on_child_exit;
options.file = args[0];
options.args = args;
if (uv_spawn(loop, &child_req, options))
fprintf(stderr, "%s\n", uv_strerror(uv_last_error(loop)));
return 1;
uv_read_start((uv_stream_t*)&apipe, alloc_buffer, read_apipe);
return uv_run(loop, UV_RUN_DEFAULT);
dummy.c:
#include <unistd.h>
#include <stdio.h>
int main()
printf("child starting\n");
fflush(stdout);
sleep(1);
printf("child running\n");
fflush(stdout);
sleep(2);
printf("child ending\n");
fflush(stdout);
return 0;
【讨论】:
【参考方案2】:在 libuv 单元测试libuv/test/test-stdio-over-pipes.c 中查看他们是如何做到的:
请勿拨打uv_pipe_open
儿童标准输入的标志:UV_CREATE_PIPE | UV_READABLE_PIPE
孩子的标准输出和标准错误的标志:UV_CREATE_PIPE | UV_WRITABLE_PIPE
Windows 上还有一个issue,其中uv_spawn
可能会在遇到错误时返回零,在这种情况下,您需要检查process.spawn_error
,它只存在于Windows 上。
【讨论】:
以上是关于使用 libuv 捕获子进程的标准输出的主要内容,如果未能解决你的问题,请参考以下文章