Как подключиться к процессу с помощью vala/glib

Я пытаюсь передать вывод из эха в команду, используя метод spawn_command_line_sync GLib. Проблема, с которой я столкнулся, заключается в том, что эхо интерпретирует всю команду как аргумент.

Чтобы лучше объяснить, я запускаю это в своем коде:

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.


person Plays2    schedule 26.01.2019    source источник


Ответы (3)


Проблема в том, что вы предоставляете синтаксис оболочки тому, что по сути является системным вызовом ядра exec(). Оператор shell pipe перенаправляет стандартный вывод одного процесса на стандартный ввод следующего. Чтобы реализовать это с помощью Vala, вам нужно получить дескриптор файла для стандартного ввода процесса command, который вы запускаете, и записать в него some_var вручную.

person Philip Withnall    schedule 26.01.2019
comment
Да, я надеялся, что смогу избежать использования spawn_async_with_pipes здесь, но, похоже, не могу. Я немного обновлю это с полной реализацией, спасибо. - person Plays2; 26.01.2019

Вы объединяете два подпроцесса в один. Вместо этого echo и command следует обрабатывать отдельно и устанавливать между ними канал. По какой-то причине во многих примерах на Stack Overflow и других сайтах используются функции Process.spawn_*, но использование GSubprocess является более простым синтаксисом.

В этом примере выходные данные 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 из первой команды как InputStream и соединяет его с OutputStream (STDIN) для второй команды.

Функция read_piped_commands получает вывод с конца канала. Это InputStream, заключенный в DataInputStream, чтобы предоставить доступ к вспомогательному методу read_line_async.

person AlThomas    schedule 27.01.2019

Вот полная рабочая реализация:

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 - это то, что действительно усложнило понимание этого. Оказалось довольно прямолинейно.

person Plays2    schedule 26.01.2019