3

我正在尝试使用 GLib 的spawn_command_line_sync方法将 echo 的输出通过管道传输到命令中。我遇到的问题是 echo 将整个命令解释为参数。

为了更好地解释,我在我的代码中运行它:

string command = "echo \"" + some_var + "\" | command";
Process.spawn_command_line_sync (command.escape (), 
                                 out r, out e, out s);

我希望将变量回显到管道,并且命令使用管道传输的数据运行,但是当我检查结果时,它只是在回显之后回显所有内容,如下所示:

"some_var's value" | command

我想我可以只使用Posix该类来运行命令,但我喜欢让结果、错误和状态值来收听该spawn_command_line_sync方法提供的值。

4

4 回答 4

5

问题是您为本质上是内核的exec()系统调用提供了 shell 语法。shell pipe 操作符将一个进程的标准输出重定向到下一个进程的标准输入。command要使用 Vala 实现它,您需要获取正在运行的进程的标准输入的文件描述符,并some_var手动写入。

于 2019-01-26T19:57:22.170 回答
5

您正在将两个子流程合二为一。相反echocommand应该分开处理,并在它们之间设置一个管道。出于某种原因,Stack Overflow 和其他站点上的许多示例都使用了这些Process.spawn_*函数,但使用 GSubprocess 是一种更简单的语法。

此示例通过管道传输 to 的输出find .sort然后将输出打印到控制台。该示例有点长,因为它是一个完整的示例,并且使用 GMainContext 进行异步调用。GMainContext 被 GMainLoop、GApplication 和 GtkApplication 使用:

void main () {
    var mainloop = new MainLoop ();
    SourceFunc quit = ()=> {
        mainloop.quit ();
        return Source.REMOVE;
    };
    read_piped_commands.begin ("find .", "sort", quit);
    mainloop.run ();
}

async void read_piped_commands (string first_command, string second_command, SourceFunc quit) {
    var output = splice_subprocesses (first_command, second_command);
    try {
        string? line = null;
        do {
            line = yield output.read_line_async ();
            print (@"$(line ?? "")\n");
            }
        while (line != null);
    } catch (Error error) {
        print (@"Error: $(error.message)\n");
    }
    quit ();
}

DataInputStream splice_subprocesses (string first_command, string second_command) {
    InputStream end_pipe = null;
    try {
        var first = new Subprocess.newv (first_command.split (" "), STDOUT_PIPE);
        var second = new Subprocess.newv (second_command.split (" "), STDIN_PIPE | STDOUT_PIPE);

        second.get_stdin_pipe ().splice (first.get_stdout_pipe (), CLOSE_TARGET);
        end_pipe = second.get_stdout_pipe ();
    } catch (Error error) {
        print (@"Error: $(error.message)\n");
    }
    return new DataInputStream (end_pipe);
}

它是splice_subprocesses回答您问题的功能。它将第一个命令中的 STDOUT 作为 an并将其与第二个命令的 (STDIN)InputStream拼接起来。OutputStream

read_piped_commands函数从管道末端获取输出。这是一个InputStream已被包装在 aDataInputStream中以提供访问read_line_async便利方法的方法。

于 2019-01-27T18:31:30.287 回答
3

这是完整的工作实现:

try {
    string[] command = {"command", "-options", "-etc"};
    string[] env = Environ.get ();
    Pid child_pid;
    string some_string = "This is what gets piped to stdin"

    int stdin;
    int stdout;
    int stderr;

    Process.spawn_async_with_pipes ("/",
        command,
        env,
        SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
        null,
        out child_pid,
        out stdin,
        out stdout,
        out stderr);

    FileStream input = FileStream.fdopen (stdin, "w");
    input.write (some_string.data);

    /* Make sure we close the process using it's pid */
    ChildWatch.add (child_pid, (pid, status) => {
        Process.close_pid (pid);
    });
} catch (SpawnError e) {
    /* Do something w the Error */
}

我猜想玩 FileStream 是真的很难弄清楚这一点。结果很简单。

于 2019-01-26T20:36:19.253 回答
0

根据先前的答案,一个有趣的案例可能是使用程序参数来让一个通用应用程序通过管道传输任何输入:

管道.vala:

void main (string[] args) {
    try {
        string command = args[1];
        var subproc = new Subprocess(STDIN_PIPE | STDOUT_PIPE, command);

        var data = args[2].data;
        var input = new MemoryInputStream.from_data(data, GLib.free);

        subproc.get_stdin_pipe ().splice (input, CLOSE_TARGET);
        var end_pipe = subproc.get_stdout_pipe ();
        var output = new DataInputStream (end_pipe);

        string? line = null;
        do {
            line = output.read_line();
            print (@"$(line ?? "")\n");
        } while (line != null);
    } catch (Error error) {
        print (@"Error: $(error.message)\n");
    }
}

建造:

$ valac --pkg gio-2.0 pipe.vala

并运行:

$ ./pipe sort "cc
ab
aa
b
"

输出:

aa
ab
b
cc
于 2021-12-07T12:19:00.550 回答