交互式 popen() Lua 调用

发布于 2024-12-25 02:16:16 字数 1528 浏览 0 评论 0原文

我正在尝试创建一个在后台运行 shell 的程序,并向其发送用户命令以执行并返回结果。这是代码:

--note: this runs on windows but I assume replacing "cmd" with "sh" it can run on linux as well
exe,err=io.popen("cmd > stdout.txt 2> stderr.txt");
if not exe then
    print("Could not run command. Error: "..err)
    return
else
    print("Command run successfully... ready!")
end

stdout,err=io.open("stdout.txt","r")
if not stdout then print("Could not open stdout: "..err) return end
stderr,err=io.open("stderr.txt","r")
if not stdout then print("Could not open stderr: "..err) return end

function execute(str)
    exe:write(str)
    return stdout:read("*all") or stderr:read("*all") or "nil"
end

repeat
    print("COMMAND: ")
    userinput=io.read("*line")
    print("You entered: '"..userinput.."'")
    if userinput=="" then print "Empty line! Exiting program..." break end
    print("Result: "..execute(userinput))
until true

print "Closing..."
execute("exit")
print "1"
exe:close()
print "2"
stdout:close()
print "3"
stderr:close()
print "Finished!"

问题:退出程序时,它挂在 exe:close() 调用上。执行循环的行为也很奇怪(有时我必须按几次 Enter 键才能 userinput=io.read("*line") 工作。

我用 google 搜索了一下 file:close() 是否也适用文件句柄是 io.popen() 的结果,但没有找到任何内容,但该调用并没有失败,换句话说,程序的输出如下:

Command run successfully... ready!
COMMAND: 
dir

dir
You entered: 'dirdir'
Result: Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\lua>
C:\lua>
C:\lua>
Closing...
1

I'm trying to create a program that runs a shell on the background and sends user commands to it for execution and return the result. This is the code:

--note: this runs on windows but I assume replacing "cmd" with "sh" it can run on linux as well
exe,err=io.popen("cmd > stdout.txt 2> stderr.txt");
if not exe then
    print("Could not run command. Error: "..err)
    return
else
    print("Command run successfully... ready!")
end

stdout,err=io.open("stdout.txt","r")
if not stdout then print("Could not open stdout: "..err) return end
stderr,err=io.open("stderr.txt","r")
if not stdout then print("Could not open stderr: "..err) return end

function execute(str)
    exe:write(str)
    return stdout:read("*all") or stderr:read("*all") or "nil"
end

repeat
    print("COMMAND: ")
    userinput=io.read("*line")
    print("You entered: '"..userinput.."'")
    if userinput=="" then print "Empty line! Exiting program..." break end
    print("Result: "..execute(userinput))
until true

print "Closing..."
execute("exit")
print "1"
exe:close()
print "2"
stdout:close()
print "3"
stderr:close()
print "Finished!"

Problem: when exiting the program, it hangs on exe:close() call. The execution loop behaves weird too (sometimes I have to press enter several times for userinput=io.read("*line") to work.

I googled to see if file:close() also works on the file handle that is the result of io.popen() but didn't find anything. But that call doesn't fail. It just hangs up. In other words the output of the program is like this:

Command run successfully... ready!
COMMAND: 
dir

dir
You entered: 'dirdir'
Result: Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\lua>
C:\lua>
C:\lua>
Closing...
1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你げ笑在眉眼 2025-01-01 02:16:16

Lua 仅依赖于 ANSI C 功能。因此 io.popen 使用 popen(3)功能。引用它的手册:

由于管道根据定义是单向的,因此类型参数只能指定读取或写入,而不能同时指定两者;生成的流相应地是只读或只写的。

您尝试通过将输出重定向到文件并在执行命令后同时打开该文件并从中读取来解决此限制。但是,在这种情况下,您可能会遇到输出缓冲问题 - 我认为这就是您所遇到的问题。

您可以尝试使用 Lua Ex API,而不是尝试解决 io.popen wiki 页面),它提供了替代的进程生成 API,并允许您执行以下操作:

-- popen2(), from http://lua-users.org/wiki/ExtensionProposal
function popen2(...)
  local in_rd, in_wr = io.pipe()
  local out_rd, out_wr = io.pipe()
  local proc, err = os.spawn{stdin = in_rd, stdout = out_wr, ...}
  in_rd:close(); out_wr:close()
  if not proc then
    in_wr:close(); out_rd:close()
    return proc, err
  end
  return proc, out_rd, in_wr
end
-- usage:
local p, i, o = assert(popen2("wc", "-w"))
o:write("Hello world"); o:close()
print(i:read"*l"); i:close()
p:wait()

Lua only depends on ANSI C features. Therefore io.popen uses the popen(3) function. Quoting from it's manual:

Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.

You attempt to solve this limitation by redirecting the output to a file and simultaneously open that file and read from it after executing a command. However, in this case you may run into problems with output buffering - I think this is what you are experiencing.

Instead of trying to work around io.popen, you may try out the Lua Ex API (wiki page here), which provides an alternative process spawning API and allows you to do things like this:

-- popen2(), from http://lua-users.org/wiki/ExtensionProposal
function popen2(...)
  local in_rd, in_wr = io.pipe()
  local out_rd, out_wr = io.pipe()
  local proc, err = os.spawn{stdin = in_rd, stdout = out_wr, ...}
  in_rd:close(); out_wr:close()
  if not proc then
    in_wr:close(); out_rd:close()
    return proc, err
  end
  return proc, out_rd, in_wr
end
-- usage:
local p, i, o = assert(popen2("wc", "-w"))
o:write("Hello world"); o:close()
print(i:read"*l"); i:close()
p:wait()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文