3
votes

Comment diriger vers un processus à l'aide de vala / glib

J'essaye de diriger la sortie de l'écho dans une commande en utilisant la méthode spawn_command_line_sync de GLib. Le problème que j'ai rencontré est que l'écho interprète la commande entière comme argument.

Pour mieux expliquer, j'exécute ceci dans mon code:

"some_var's value" | command

Je m'attendrais la variable à envoyer en écho au tube et la commande s'exécute avec les données acheminées, mais quand je vérifie le résultat, cela fait simplement écho à tout après l'écho comme ceci:

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

Je pense Je pourrais simplement utiliser la classe Posix pour exécuter la commande, mais j'aime avoir le résultat, l'erreur et les valeurs de statut à écouter que la méthode spawn_command_line_sync fournit.


3 Réponses :


4
votes

Le problème est que vous fournissez une syntaxe shell à ce qui est essentiellement l'appel système exec () du noyau. L'opérateur de canal shell redirige le stdout d'un processus vers le stdin du suivant. Pour implémenter cela en utilisant Vala, vous devez obtenir le descripteur de fichier pour le stdin du processus command que vous exécutez, et y écrire some_var manuellement.


1 commentaires

Ouais, j'espérais pouvoir éviter d'utiliser spawn_async_with_pipes ici mais je suppose que je ne peux pas. Je vais mettre à jour cela avec l'implémentation complète dans un peu, merci.



3
votes

Voici l'implémentation complète et fonctionnelle:

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 */
}

Je suppose que jouer avec FileStream est ce qui a vraiment rendu difficile de comprendre cela. S'est avéré être assez simple.


0 commentaires

5
votes

Vous combinez deux sous-processus en un seul. Au lieu de cela, echo et command doivent être traités séparément et avoir un tube installé entre eux. Pour une raison quelconque, de nombreux exemples sur Stack Overflow et d'autres sites utilisent les fonctions Process.spawn_ * , mais l'utilisation de GSubprocess est une syntaxe plus simple.

Cet exemple redirige la sortie de find. code> pour trier , puis imprime la sortie sur la console. L'exemple est un peu plus long car il s'agit d'un exemple pleinement fonctionnel et utilise un GMainContext pour les appels asynchrones. GMainContext est utilisé par GMainLoop, GApplication et 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);
}

C'est la fonction splice_subprocesses qui répond à votre question. Il prend le STDOUT de la première commande comme un InputStream et le joint avec le OutputStream (STDIN) pour la deuxième commande.

Le La fonction read_piped_commands prend la sortie de la fin du tube. Il s'agit d'un InputStream qui a été enveloppé dans un DataInputStream pour donner accès à la méthode pratique read_line_async .


0 commentaires