22

I need to use Lua to run a binary program that may write something in its stdout and also returns a status code (also known as "exit status").

I searched the web and couldn't find something that does what I need. However I found out that in Lua:

  • os.execute() returns the status code
  • io.popen() returns a file handler that can be used to read process output

However I need both. Writing a wrapper function that runs both functions behind the scene is not an option because of process overhead and possibly changes in result on consecutive runs. I need to write a function like this:

function run(binpath)
    ...
    return output,exitcode
end

Does anyone has an idea how this problem can be solved?

PS. the target system rung Linux.

AlexStack
  • 16,766
  • 21
  • 72
  • 104

6 Answers6

22

With Lua 5.2 I can do the following and it works

-- This will open the file
local file = io.popen('dmesg')
-- This will read all of the output, as always
local output = file:read('*all')
-- This will get a table with some return stuff
-- rc[1] will be true, false or nil
-- rc[3] will be the signal
local rc = {file:close()}

I hope this helps!

qaisjp
  • 722
  • 8
  • 31
aignas
  • 1,044
  • 12
  • 19
  • 2
    Sadly, this doesn't work inside NGINX :/, see: https://github.com/openresty/lua-nginx-module/issues/779 – jirislav May 27 '18 at 14:51
5

I can't use Lua 5.2, I use this helper function.

function execute_command(command)
    local tmpfile = '/tmp/lua_execute_tmp_file'
    local exit = os.execute(command .. ' > ' .. tmpfile .. ' 2> ' .. tmpfile .. '.err')

    local stdout_file = io.open(tmpfile)
    local stdout = stdout_file:read("*all")

    local stderr_file = io.open(tmpfile .. '.err')
    local stderr = stderr_file:read("*all")

    stdout_file:close()
    stderr_file:close()

    return exit, stdout, stderr
end
David Qin
  • 355
  • 1
  • 3
  • 8
  • 3
    You could use `os.tmpname()` to get a temporary file name, independent of your OS, and be sure to have write access. In this case, it must be removed after use (os.remove). If you're stuck with lua 5.1, this is the best solution I've found for this issue (the main drawback is you can't stream the output for long running processes, and/or those who have a massive output) – youen Mar 01 '19 at 08:59
4

This is how I do it.

local process = io.popen('command; echo $?') -- echo return code of last run command
local lastline
for line in process:lines() do
    lastline = line
end
print(lastline) -- the return code is the last line of output

If the last line has fixed length you can read it directly using file:seek("end", -offset), offset should be the length of the last line in bytes.

Jian
  • 3,118
  • 2
  • 22
  • 36
  • Is it possible to get the last line directly? The `process:lines()` seems to be a function so I don't know how to do. – NeoZoom.lua Jan 17 '22 at 13:40
  • Only if the last line has fixed length can you use [file:seek](https://www.lua.org/manual/5.1/manual.html#pdf-file:seek) @Rainning – Jian Jan 18 '22 at 04:38
3

This functionality is provided in C by pclose.

Upon successful return, pclose() shall return the termination status of the command language interpreter.

The interpreter returns the termination status of its child.

But Lua doesn't do this right (io.close always returns true). I haven't dug into these threads but some people are complaining about this brain damage.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • @AlexStack I hope you find a way around. Lua is nice but development seems kind of slow (the first thread is from 2004!). – cnicutar Sep 30 '11 at 08:13
1

If you're running this code on Win32 or in a POSIX environment, you could try this Lua extension: http://code.google.com/p/lua-ex-api/

Alternatively, you could write a small shell script (assuming bash or similar is available) that:

  • executes the correct executable, capturing the exit code into a shell variable,
  • prints a newline and terminal character/string onto standard out
  • prints the shell variables value (the exit code) onto standard out

Then, capture all the output of io.popen and parse backward.

Full disclosure: I'm not a Lua developer.

Ryan Schipper
  • 1,019
  • 7
  • 8
  • Thank you. Yes we do have a shell on our little Linux system. This is the fastest solution to the problem. – AlexStack Sep 30 '11 at 08:13
-2

yes , your are right that os.execute() has returns and it's very simple if you understand how to run your command with and with out lua you also may want to know how many variables it returns , and it might take a while , but i think you can try

local a, b, c, d, e=os.execute(-what ever your command is-)

for my example a is an first returned argument , b is the second returned argument , and etc.. i think i answered your question right, based off of what you are asking.

joshua chris
  • 55
  • 12