Search code examples
javascriptlinuxgnome-shell-extensions

Multiple arguments in Gio.Subprocess


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?


Solution

  • 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);
        }
    }