所以我正在进行Gnome Shell扩展。我希望能够用管道运行一些命令。 (该命令实际上是"xrandr --query | awk 'something'"
,但这不是主题)
所以,到目前为止我所做的是
GLib.spawn_async_with_pipes(null,
['/usr/bin/xrandr', '--query', '|', 'awk...'], null,
GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);
但它不起作用! 我找不到任何在带管道的gnome扩展中运行命令的例子。
我是否必须像我一样在命令中编写"|"
?
答案 0 :(得分:0)
spawn_async_with_pipes
没有做你想做的事(以简单的方式)。它返回管道进行处理。你可以通过两次调用和连接来完成它,但它会有点复杂。
保持确切语法的一种简单方法是调用将执行管道处理的shell 在this answer的帮助下,它提供了一种调用命令的方法,我编写了下面的代码,用正确的参数调用shell(这种情况下是bash)
const Util = imports.misc.util;
Util.spawn(['/bin/bash', '-c', "xrandr --query | awk 'something'"])
答案 1 :(得分:0)
我前段时间在Cinnamon Applet中实现了一个TerminalReader类:https://github.com/lestcape/Configurable-Menu/blob/OwnAPI/configurableMenu%40lestcape/pakagesManager.js#L31
此类现在也在其他地方使用,因此您有更多示例可以更好地使用它:https://github.com/search?l=JavaScript&q=TerminalReader&type=Code&utf8=%E2%9C%93
以下是该类的源代码:
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());
}
}));
}
};
答案 2 :(得分:0)
调用spawn_async_with_pipes()
不会对您有帮助,因为它会给您返回一个对象,该对象具有用于stdin / stdout / stderr的可用管道,而不是给您相互传递的调用。
除了仅调用shell实例并让其执行命令外,唯一的方法就是坚持扩展本身,让GNOME使用临时文件和默认shell(如果已有的话将覆盖自身)来处理所有内容。
对于示例代码,让我们做与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]
现在可以随意处理您拥有的数据,因为命令完成后所有内容都会退出。
别忘了使用file[1].close();
关闭文件,并在完成后将所有剩余变量设置为null
。