简体   繁体   English

Gnome shell扩展:如何使用管道运行命令

[英]Gnome shell extensions: how to run a command with pipes

So I'm making a Gnome Shell extension. 所以我正在制作Gnome Shell扩展。 And I want to be able to run some command with a pipe. 我希望能够用管道运行一些命令。 (The command is actually "xrandr --query | awk 'something'" , but that is off topic) (该命令实际上是"xrandr --query | awk 'something'" ,但这不是主题)

So, what I have done so far is 所以,到目前为止我所做的是

GLib.spawn_async_with_pipes(null,
                            ['/usr/bin/xrandr', '--query', '|', 'awk...'], null,
                            GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);

But it doesn't work! 但它不起作用! I can't find any example of running a command in a gnome extensions with a pipe. 我找不到任何在带有管道的gnome扩展中运行命令的示例。

Do I have to write "|" 我必须写"|" in the command like I did ? 像我一样在命令中?

spawn_async_with_pipes doesn't do what you want (in a simple way). spawn_async_with_pipes没有做你想要的(以一种简单的方式)。 It return the pipes to process with it. 它返回管道进行处理。 You could do it with two call and connecting but it will be a little bit complex. 你可以通过两个调用和连接来完成它,但它会有点复杂。

A simple way to keep the exact syntax is to call a shell which will do the pipe processing with the help of this answer which give a way to call a command, I wrote the following code which call the shell (bash for this case) with correct arguments 保持确切语法的一种简单方法是调用一个shell,它将在这个回答的帮助下进行管道处理,这给出了一种调用命令的方法,我编写了下面的代码来调用shell(本例中为bash)正确的论点

const Util = imports.misc.util;
Util.spawn(['/bin/bash', '-c', "xrandr --query | awk 'something'"])

I implemented a TerminalReader class some time ago in a Cinnamon Applet: https://github.com/lestcape/Configurable-Menu/blob/OwnAPI/configurableMenu%40lestcape/pakagesManager.js#L31 我不久前在Cinnamon Applet中实现了一个TerminalReader类: https//github.com/lestcape/Configurable-Menu/blob/OwnAPI/configurableMenu%40lestcape/pakagesManager.js#L31

This class is now used in other places also, so you have more examples to underestand it better: https://github.com/search?l=JavaScript&q=TerminalReader&type=Code&utf8=%E2%9C%93 这个类现在也在其他地方使用,所以你有更多的例子可以更好地使用它: https//github.com/search?l = JavaScript&q = TermReader&type = Code = utf8 =%E2%9C%93

Here is the source code of the class: 这是该类的源代码:

function TerminalReader() {
    this._init.apply(this, arguments);
}

TerminalReader.prototype = {
_init: function(command, callback) {
    this._callbackPipe = callback;
    this._commandPipe = command;
    this.idle = true;
    this._childWatch = null;
},

executeReader: function() {
    if(this.idle) {
        this.idle = false;
        try {
            let [success, argv] = GLib.shell_parse_argv("sh -c '" + this._commandPipe + "'");
            if(success) {
                let [exit, pid, stdin, stdout, stderr] =
                  GLib.spawn_async_with_pipes(
                      null, // cwd
                      argv, // args
                      null, // env
                      GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, //Use env path and no repet
                      null // child_setup
                  );

                this._childPid = pid;
                this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
                this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
                this._stderr = new Gio.UnixInputStream({ fd: stderr, close_fd: true });

                // We need this one too, even if don't actually care of what the process
                // has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
                // is kept open indefinitely
                this._stderrStream = new Gio.DataInputStream({ base_stream: this._stderr });
                this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
                this._cancellableStderrStream = new Gio.Cancellable();
                this._cancellableStdout = new Gio.Cancellable();

                this.resOut = 1;
                this._readStdout();
                this.resErr = 1;
                this._readStderror();

                this._childWatch = GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, Lang.bind(this, function(pid, status, requestObj) {
                    GLib.source_remove(this._childWatch);
                    this._childWatch = null;
                    this._stdin.close(null);
                    this.idle = true;
                }));
            }
            //throw
        } catch(err) {
            if(err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
                err.message = _("Command not found.");
            } else {
                // The exception from gjs contains an error string like:
                //   Error invoking GLib.spawn_command_line_async: Failed to
                //   execute child process "foo" (No such file or directory)
                // We are only interested in the part in the parentheses. (And
                // we can't pattern match the text, since it gets localized.)
                err.message = err.message.replace(/.*\((.+)\)/, '$1');
            }
            throw err;
        }
    }
},

destroy: function() {
    try {
        if(this._childWatch) {
            GLib.source_remove(this._childWatch);
            this._childWatch = null;
        }
        if(!this._dataStdout.is_closed()) {
            this._cancellableStdout.cancel();
            this._stdout.close_async(0, null, Lang.bind(this, this.closeStdout));
        }
        if(!this._stderrStream.is_closed()) {
            this._cancellableStderrStream.cancel();
            this._stderrStream.close_async(0, null, Lang.bind(this, this.closeStderrStream));
        }
        this._stdin.close(null);
        this.idle = true;
    }
    catch(e) {
        Main.notify("Error on close" + this._dataStdout.is_closed(), e.message);
    }
},

closeStderrStream: function(std, result) {
    try {
        std.close_finish(result);
    } catch(e) {
        std.close_async(0, null, Lang.bind(this, this.closeStderrStream));
    }
},

closeStdout: function(std, result) {
    try {
        std.close_finish(result);
    } catch(e) {
        std.close_async(0, null, Lang.bind(this, this.closeStderrStream));
    }
},

_readStdout: function() {
    this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, this._cancellableStdout, Lang.bind(this, function(stream, result) {
        try {
            if(!this._dataStdout.is_closed()) {
                if(this.resOut != -1)
                    this.resOut = this._dataStdout.fill_finish(result);// end of file
                if(this.resOut == 0) {
                    let val = stream.peek_buffer().toString();
                    if(val != "")
                        this._callbackPipe(this._commandPipe, true, val);
                    this._stdout.close(this._cancellableStdout);
                } else {
                    // Try to read more
                    this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
                    this._readStdout();
                }
            }
        } catch(e) {
            global.log(e.toString());
        }
    }));
},

_readStderror: function() {
    this._stderrStream.fill_async(-1, GLib.PRIORITY_DEFAULT, this._cancellableStderrStream, Lang.bind(this, function(stream, result) {
        try {
            if(!this._stderrStream.is_closed()) {
                if(this.resErr != -1)
                    this.resErr = this._stderrStream.fill_finish(result);
                if(this.resErr == 0) { // end of file
                    let val = stream.peek_buffer().toString();
                    if(val != "")
                        this._callbackPipe(this._commandPipe, false, val);
                    this._stderr.close(null);
                } else {
                    this._stderrStream.set_buffer_size(2 * this._stderrStream.get_buffer_size());
                    this._readStderror();
                }
            }
        } catch(e) {
            global.log(e.toString());
        }
    }));
}
};

calling spawn_async_with_pipes() isn't going to help you, as it gives you back an object with availlable pipes for stdin/stdout/stderr rather than giving you calls that are piped to one another. 调用spawn_async_with_pipes()不会对你有帮助,因为它会为你提供一个带有stdin / stdout / stderr的可用管道的对象,而不是给你一个彼此管道的调用。 Except for just calling a shell instance and letting it execute commands, the only way is to stick to the extension itself, letting GNOME handle everything using a temp file and default shell (one that overwrites itself if it already exists). 除了只调用一个shell实例并让它执行命令之外,唯一的办法是坚持扩展本身,让GNOME使用临时文件和默认shell(如果它已经存在则覆盖自己)来处理所有内容。

For the example code, lets do the same as journalctl | grep -i js 对于示例代码,让我们像journalctl | grep -i js journalctl | grep -i js would do on a CLI: journalctl | grep -i js会在CLI上执行:

//Gio for File I/O and GLib for command execution
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
//Create an object for the temp file
let file = Gio.file_new_tmp(null);
//Now write the output of journalctl to the file
file[1].get_output_stream().write(GLib.spawn_command_line_sync("journalctl")[1], null);
//Execute the command and save the result, this locks the thread though,
//so don't use indefinite commands like journalctl -f
//This returns [Boolean success, String stdout, String stderr, Number exit_status]
let journalJS = GLib.spawn_command_line_sync('grep -i js ' + file[0].get_path())[1]

Now feel free to do with the data you have, as everything exits after the command is finished. 现在可以随意处理您拥有的数据,因为在命令完成后一切都会退出。

Don't forget to close the file using file[1].close(); 不要忘记使用file[1].close();关闭文件file[1].close(); and setting any remaining variables to null when finished. 完成后将任何剩余变量设置为null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM