5

I am writing a simple extension to open browser by clicking the extension button. I would like to know if there is a function which can execute passed shell command as argument. Also, it'd be really helpful if anyone can suggest a good simple reference for extension development.

Didier L
  • 18,905
  • 10
  • 61
  • 103
Sam Fischer
  • 51
  • 1
  • 6

2 Answers2

10

From https://github.com/GNOME/gnome-shell/blob/master/js/misc/util.js:

// Runs @command_line in the background, handling any errors that
// occur when trying to parse or start the program.
function spawnCommandLine(command_line) {
    try {
        let [success, argv] = GLib.shell_parse_argv(command_line);
        trySpawn(argv);
    } catch (err) {
        _handleSpawnError(command_line, err);
    }
}

There are a few variations on that method in there. Save yourself mountains of headaches and just bookmark the GitHub repository.

Some quick links:

  • popupMenu.js: working with popup menus
  • panel.js: a good read for implementing "tray" icons
  • modalDialog.js: some UI elements were made to be reused, runDialog.js uses this for example
  • mpris.js: there are also good examples of using frameworks like DBus in gjs

I can't stress enough how much you'll get out of reading the gnome-shell source. Unfortunately, it's compiled into a resource file now so we don't have local copies to stumble upon.

UPDATE (2021)

If you're reading this, please instead see the documentation available on gjs.guide. Specifically the documentation on Spawning Subprocesses, which covers why this is a bad idea in extensions and how to do it slightly less bad.

andy.holmes
  • 3,383
  • 17
  • 28
  • I'm missing a way to use these functions in my extension without copy & paste. Is there any? – raphinesse Feb 05 '18 at 22:47
  • 4
    You just import them as modules then instantiate or subclass the Classes and call the functions: `const Util = imports.misc.util; Util.spawnCommandLine("...");` – andy.holmes Feb 06 '18 at 04:45
  • What should be done if the command requires `sudo`? Do you include `sudo` in your command, or will Gnome ask for password window-style? – Stewart Oct 13 '18 at 17:50
  • If it's not a GUI program that can request permissions itself, you should first examine whether that's a good idea since your user may not have feedback as to the results. But if you're sure it's a good idea, you would probably have to use `pkexec` or maybe `gksudo`. – andy.holmes Oct 15 '18 at 07:46
  • @andy.holmes any idea how to get the output of the bash command we spawn? – kartheek7895 Jun 13 '20 at 12:21
  • 1
    @kartheek7895 Use Gio.Subprocess instead: https://andyholmes.github.io/articles/subprocesses-in-gjs.html#communicating-with-processes – andy.holmes Jun 13 '20 at 20:08
  • This is exactly what I was looking for, `communicate_utf8(stdin_buf, cancellable)` does the job for me – kartheek7895 Jun 13 '20 at 22:19
  • I think you should add the usage information (imports + invoking `Utils` functions) to the answer, as it is not obvious that these functions should be just imported without reading the comments. – Didier L May 10 '21 at 21:20
0

If you're not interested in the result - i.e. when you want to open a browser window - you can just use GLib.spawn_command_line_async like so:

const GLib = imports.gi.GLib;
...
(this._menuEntries[i]).connect('activate', () => {
       GLib.spawn_command_line_async('firefox http://example.com?p='+ my_params[i]);
});

If you need a synchronous result, read https://gjs.guide/guides/gio/subprocesses.html

Rainer Feike
  • 191
  • 1
  • 9