Add awful.util.spawn_with_line_callback
This new function spawns a program, similarly to awful.spawn, but captures its output. On each line of output on stdout / stderr, a Lua function is called with this line. There are different callbacks for stdout and stderr. When both stdout and stderr are closed, another callback function is called. The intention for this last callback is "the program is done", because most programs should only close their output when they exit. Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
1beda989e5
commit
0e20fef2bd
|
@ -22,6 +22,8 @@ local pairs = pairs
|
||||||
local string = string
|
local string = string
|
||||||
local lgi = require("lgi")
|
local lgi = require("lgi")
|
||||||
local Pango = lgi.Pango
|
local Pango = lgi.Pango
|
||||||
|
local Gio = lgi.Gio
|
||||||
|
local GLib = lgi.GLib
|
||||||
local capi =
|
local capi =
|
||||||
{
|
{
|
||||||
awesome = awesome,
|
awesome = awesome,
|
||||||
|
@ -110,6 +112,94 @@ function util.spawn_with_shell(cmd, sn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Spawn a program and asynchronously capture its output line by line.
|
||||||
|
-- @tparam string|table cmd The command.
|
||||||
|
-- @tparam[opt] function stdout_callback Function that is called with each line of
|
||||||
|
-- output on stdout, e.g. `stdout_callback(line)`.
|
||||||
|
-- @tparam[opt] function stderr_callback Function that is called with each line of
|
||||||
|
-- output on stderr, e.g. `stderr_callback(line)`.
|
||||||
|
-- @tparam[opt] function done_callback Function to call when no more output is
|
||||||
|
-- produced.
|
||||||
|
-- @treturn[1] Integer the PID of the forked process.
|
||||||
|
-- @treturn[2] string Error message.
|
||||||
|
function util.spawn_with_line_callback(cmd, stdout_callback, stderr_callback, done_callback)
|
||||||
|
local have_stdout, have_stderr = stdout_callback ~= nil, stderr_callback ~= nil
|
||||||
|
local pid, sn_id, stdin, stdout, stderr = capi.awesome.spawn(cmd, false, false, have_stdout, have_stderr)
|
||||||
|
if type(pid) == "string" then
|
||||||
|
-- Error
|
||||||
|
return pid
|
||||||
|
end
|
||||||
|
|
||||||
|
local done_before = false
|
||||||
|
local function step_done()
|
||||||
|
if have_stdout and have_stderr and not done_before then
|
||||||
|
done_before = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
done_callback()
|
||||||
|
end
|
||||||
|
if have_stdout then
|
||||||
|
util.read_lines(Gio.UnixInputStream.new(stdout, true),
|
||||||
|
stdout_callback, step_done, true)
|
||||||
|
end
|
||||||
|
if have_stderr then
|
||||||
|
util.read_lines(Gio.UnixInputStream.new(stderr, true),
|
||||||
|
stderr_callback, step_done, true)
|
||||||
|
end
|
||||||
|
assert(stdin == nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Read lines from a Gio input stream
|
||||||
|
-- @tparam Gio.InputStream input_stream The input stream to read from.
|
||||||
|
-- @tparam function line_callback Function that is called with each line
|
||||||
|
-- read, e.g. `line_callback(line_from_stream)`.
|
||||||
|
-- @tparam[opt] function done_callback Function that is called when the
|
||||||
|
-- operation finishes (e.g. due to end of file).
|
||||||
|
-- @tparam[opt=false] boolean close Should the stream be closed after end-of-file?
|
||||||
|
function util.read_lines(input_stream, line_callback, done_callback, close)
|
||||||
|
local stream = Gio.DataInputStream.new(input_stream)
|
||||||
|
local function done()
|
||||||
|
if close then
|
||||||
|
stream:close()
|
||||||
|
end
|
||||||
|
if done_callback then
|
||||||
|
xpcall(done_callback, function(err)
|
||||||
|
print(debug.traceback("Error while calling done_callback:"
|
||||||
|
.. tostring(err), 2))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local start_read, finish_read
|
||||||
|
start_read = function()
|
||||||
|
stream:read_line_async(GLib.PRIORITY_DEFAULT, nil, finish_read)
|
||||||
|
end
|
||||||
|
finish_read = function(obj, res)
|
||||||
|
local line, length = obj:read_line_finish(res)
|
||||||
|
if type(length) ~= "number" then
|
||||||
|
-- Error
|
||||||
|
print("Error in awful.util.read_lines:", tostring(length))
|
||||||
|
done()
|
||||||
|
elseif #line ~= length then
|
||||||
|
-- End of file
|
||||||
|
done()
|
||||||
|
else
|
||||||
|
-- Read a line
|
||||||
|
xpcall(function()
|
||||||
|
-- This needs tostring() for older lgi versions which returned
|
||||||
|
-- "GLib.Bytes" instead of Lua strings (I guess)
|
||||||
|
line_callback(tostring(line))
|
||||||
|
end, function(err)
|
||||||
|
print(debug.traceback("Error while calling line_callback: "
|
||||||
|
.. tostring(err), 2))
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Read the next line
|
||||||
|
start_read()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
start_read()
|
||||||
|
end
|
||||||
|
|
||||||
--- Read a program output and returns its output as a string.
|
--- Read a program output and returns its output as a string.
|
||||||
-- @param cmd The command to run.
|
-- @param cmd The command to run.
|
||||||
-- @return A string with the program output, or the error if one occured.
|
-- @return A string with the program output, or the error if one occured.
|
||||||
|
|
Loading…
Reference in New Issue