1

I'm developing my first gnome-shell-extension currently. In the extension, I want to execute a simple shell command and use the output afterwards, for which I use Gio.Subprocess like it is used in this wiki: https://wiki.gnome.org/AndyHolmes/Sandbox/SpawningProcesses

Currently, I have an argument like this one with some parameters: "ProgramXYZ -a -bc" which I pass as the argument vector argv as ['ProgramXYZ','-a','-bc']. This case works fine.

So let's say I would like to call two programs and combine the output with your approach, like: "ProgramXYZ -a -bc && ProgramB". My current output is correct in a normal terminal, but I'm not sure how to pass it to the Gio.Subprocess. Something like ['ProgramXYZ','-a','-bc','&&','ProgramB'] does not work, is there a way to achieve that or do i have to make two seperate calls?

raujonas
  • 117
  • 8

1 Answers1

1

Sorry, I haven't managed to finish that page (that's why it's in my sandbox ).

Here is our Promise wrapper for running a subprocess:

function execCommand(argv, input = null, cancellable = null) {
    let flags = Gio.SubprocessFlags.STDOUT_PIPE;

    if (input !== null)
        flags |= Gio.SubprocessFlags.STDIN_PIPE;

    let proc = new Gio.Subprocess({
        argv: argv,
        flags: flags
    });
    proc.init(cancellable);

    return new Promise((resolve, reject) => {
        proc.communicate_utf8_async(input, cancellable, (proc, res) => {
            try {
                resolve(proc.communicate_utf8_finish(res)[1]);
            } catch (e) {
                reject(e);
            }
        });
    });
}

Now you have two reasonable choices, since you have a nice wrapper.

I would prefer this option myself, because if I'm launching sequential processes I probably want to know which failed, what the error was and so on. I really wouldn't worry about extra overhead, since the second process only executes if the first succeeds, at which point the first will have been garbage collected.

async function dualCall() {
    try {
        let stdout1 = await execCommand(['ProgramXYZ', '-a', '-bc']);
        let stdout2 = await execCommand(['ProgramB']);
    } catch (e) {
        logError(e);
    }
}

On the other hand, there is nothing preventing you from launching a sub-shell if you really want to do shell stuff. Ultimately you're just offloading the same behaviour to a shell, though:

async function shellCall() {
    try {
        let stdout = await execCommand([
            '/bin/sh',
            '-c',
            'ProgramXYZ -a -bc && ProgramB'
        ]);
    } catch (e) {
        logError(e);
    }
}
andy.holmes
  • 3,383
  • 17
  • 28
  • I have a small follow-up question about `proc.init(cancellable)` in the function `execCommand`. Currently I'm trying to execute one very long command split up as many small commands. `let commands = {"commands":[ {"ProgramXYZ -a -bc","interval":1}, {"ProgramB","interval":1},...]}` And for each: `commands.commands.forEach(command => { await execCommand(['/bin/sh', '-c', command.command]).then.... })` It seems, that the `proc.init(cancellable)` is causing a small freeze (all at ~same time) but I can't use GAsyncInitable instead, right? Are there options for doing it async? – raujonas May 12 '20 at 11:12
  • Sorry, it's pretty hard to parse the question you're asking here. Maybe open a new question? – andy.holmes May 12 '20 at 18:04
  • Hi Andy, you're totally right, I openend a new one: https://stackoverflow.com/questions/61760615/ui-freezes-for-a-short-moment-while-trying-to-execute-multiple-commands-in-a-gno Please let me know if it is still not understandable. – raujonas May 12 '20 at 19:46