使用libuv捕获子进程的stdout

时间:2013-02-07 12:48:01

标签: c spawn libuv

我正在使用libuv。我已经读过http://nikhilm.github.com/uvbook/processes.html但仍然无法弄清楚如何捕获子进程的stdout以便它在父进程中可用(但不能代替父进程的stdin)。

我的代码目前是:

#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管道的重点。

2 个答案:

答案 0 :(得分:5)

我找到了解决方案:

  1. 我有错误的标志,它们应该是UV_CREATE_PIPE | UV_READABLE_PIPE而不是UV_INHERIT_STREAM
  2. 我需要在uv_read_start之后致电uv_spawn。我认为没有数据丢失的可能,因为尚未调用uv_run。
  3. 以上两个修正显示dummy的所有输出一次到达,而不是三个块(就像它在命令行上一样)。 fflush中的dummy.c修正了此问题。
  4. 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;
    }
    

答案 1 :(得分:4)

了解他们如何在libuv单元测试libuv/test/test-stdio-over-pipes.c中执行此操作:

  • 请勿致电uv_pipe_open
  • 孩子标准的标志:UV_CREATE_PIPE | UV_READABLE_PIPE
  • 儿童stdout和stderr的标志:UV_CREATE_PIPE | UV_WRITABLE_PIPE

Windows上还有issue,即使遇到错误,uv_spawn也可能返回零,在这种情况下,您需要检查process.spawn_error,它只存在于视窗。