如何使用Lua构建读写管道?
我想做这样的事情:
foo=$(echo "$foo"|someprogram)
只是用 lua 实现。也就是说,我有一个包含大量文本的变量,我想通过一个过滤器运行它(实际上实现在 Python 中)。
有什么提示吗?
补充:真的很想做到这一点,而不使用临时文件
原文链接 https://stackoverflow.com/questions/1242572
一个不太好的解决方案,避免使用临时文件...
require("io")
require("posix")
x="hello\nworld"
posix.setenv("LUA_X",x) # 设置环境变量
i=popen('echo "$LUA_X" | myfilter') # 打开管道并执行过滤器
x=i.read("*a") # 读取执行过滤器后的结果
Aha,可能更好的解决方案:
require('posix') require('os') require('io')
function splat_popen(data,cmd) rd,wr = posix.pipe() io.flush() child = posix.fork() if child == 0 then rd:close() wr:write(data) io.flush() os.exit(1) end wr:close()
rd2,wr2 = posix.pipe() io.flush() child2 = posix.fork() if child2 == 0 then rd2:close() posix.dup(rd,io.stdin) posix.dup(wr2,io.stdout) posix.exec(cmd) os.exit(2) end wr2:close() rd:close()
y = rd2:read(“*a”) rd2:close()
posix.wait(child2) posix.wait(child)
return y end
munged = splat_popen(“hello,world”,“/usr/games/rot13”) print(“munged:”..munged..”!”)
```
只要你的 Lua 支持 io.popen
,这个问题就变得很容易。解决方案与你所概述的完全相同,除了你需要一个像这样的函数而不是 $(...)
:
function os.capture(cmd, raw)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then return s end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
然后你可以调用
local foo = ...
local cmd = ("echo $foo | someprogram"):gsub('$foo', foo)
foo = os.capture(cmd)
我经常做这样的事情。这里还有一个有用的函数用于形成命令:
local quote_me = '[^%w%+%-%=%@%_%/]' -- 补集(不需要引号)
local strfind = string.find
function os.quote(s)
if strfind(s, quote_me) or s == '' then
return "'" .. string.gsub(s, "'", [['"'"']]) .. "'"
else
return s
end
end
下面是我的解决方案,它需要lua posix
。
p = require 'posix'
local r,w = p.pipe()
local r1,w1 = p.pipe()
local cpid = p.fork()
if cpid == 0 then -- 子进程从管道中读取
w:close()
r1:close()
p.dup(r, io.stdin)
p.dup(w1 ,io.stdout)
p.exec('./myProgram')
r:close()
w1:close()
p._exit(0)
else -- 父进程向管道中写入
IN = r1
OUT = w
end
在myProgram
执行期间,您将从常规io中读取和写入,执行此代码后,您只需在IN
和OUT
上写入/读取,即可与子程序通信。
我在尝试做同样的事情时偶然发现了这篇帖子,并没有找到一个很好的解决方案,看下面的代码以了解我如何解决我的问题。此实现允许用户访问stdin、stdout、stderr并获取返回状态代码。简单的包装器用于简单的管道调用。
require("posix")
--
-- Simple popen3() implementation
--
function popen3(path, ...)
local r1, w1 = posix.pipe()
local r2, w2 = posix.pipe()
local r3, w3 = posix.pipe()
assert((r1 ~= nil or r2 ~= nil or r3 ~= nil), "pipe() failed")
local pid, err = posix.fork()
assert(pid ~= nil, "fork() failed")
if pid == 0 then
posix.close(w1)
posix.close(r2)
posix.dup2(r1, posix.fileno(io.stdin))
posix.dup2(w2, posix.fileno(io.stdout))
posix.dup2(w3, posix.fileno(io.stderr))
posix.close(r1)
posix.close(w2)
posix.close(w3)
local ret, err = posix.execp(path, unpack({...}))
assert(ret ~= nil, "execp() failed")
posix._exit(1)
return
end
posix.close(r1)
posix.close(w2)
posix.close(w3)
return pid, w1, r2, r3
end
--
-- Pipe input into cmd + optional arguments and wait for completion
-- and then return status code, stdout and stderr from cmd.
--
function pipe_simple(input, cmd, ...)
--
-- Launch child process
--
local pid, w, r, e = popen3(cmd, unpack({...}))
assert(pid ~= nil, "filter() unable to popen3()")
--
-- Write to popen3's stdin, important to close it as some (most?) proccess
-- block until the stdin pipe is closed
--
posix.write(w, input)
posix.close(w)
local bufsize = 4096
--
-- Read popen3's stdout via Posix file handle
--
local stdout = {}
local i = 1
while true do
buf = posix.read(r, bufsize)
if buf == nil or #buf == 0 then break end
stdout[i] = buf
i = i + 1
end
--
-- Read popen3's stderr via Posix file handle
--
local stderr = {}
local i = 1
while true do
buf = posix.read(e, bufsize)
if buf == nil or #buf == 0 then break end
stderr[i] = buf
i = i + 1
end
--
-- Clean-up child (no zombies) and get return status
--
local wait_pid, wait_cause, wait_status = posix.wait(pid)
return wait_status, table.concat(stdout), table.concat(stderr)
end
--
-- Example usage
--
local my_in = io.stdin:read("*all")
--local my_cmd = "wc"
--local my_args = {"-l"}
local my_cmd = "spamc"
local my_args = {} -- no arguments
local my_status, my_out, my_err = pipe_simple(my_in, my_cmd, unpack(my_args))
-- Obviously not interleaved as they would have been if printed in realtime
io.stdout:write(my_out)
io.stderr:write(my_err)
os.exit(my_status)
基于 Lua 的标准输入输出示例
这是一个非常简单的使用 Lua 编写的示例,它可以从标准输入中读取数据,并将其写入标准输出中。不需要使用额外的扩展(已测试过支持 Lua 5.3)。
#!/usr/bin/lua
-- 使用始终使用本地变量
local stdin = io.stdin:lines()
local stdout = io.write
for line in stdin do
stdout (line)
end
将其保存为 inout.lua
文件,并使用 chmod +x /tmp/inout.lua
命令将其设置为可执行文件。
以下是一个示例,展示如何使用它来读取标准输入并将其存储在 shell 变量中:
20:30 $ foo=$(echo "bla"| /tmp/inout.lua)
20:30 $ echo $foo
bla
对于一个运行着 Lua 5.1 和 luaposix 35.0-1 的系统,我从此页面的 Anthony Towns 的解决方案开始,针对此 luaposix 版本使其能够在没有任何其他加密能力的系统上调用 openssl 进行加密。我试图使代码更加明确,以便将来的 luaposix API 更改时其他人也可以处理。
local posix = require('posix');
require('os');
require('io');
local function getOutputFromProcessProvidedInput( dataForProcess, command, commandArguments )
local MAXIMUM_BYTE_READ_COUNT = 100;
local readFileHandle1,writeFileHandle1 = posix.pipe()
io.flush();
local childProcessId1 = posix.fork();
if (childProcessId1 == 0)
then
posix.close( readFileHandle1 );
posix.write( writeFileHandle1, dataForProcess );
io.flush();
os.exit( 1 );
end
posix.close( writeFileHandle1 );
local readFileHandle2,writeFileHandle2 = posix.pipe();
io.flush();
local childProcessId2 = posix.fork();
if (childProcessId2 == 0)
then
posix.close( readFileHandle2 );
posix.dup2( readFileHandle1, posix.fileno( io.stdin ) );
posix.dup2( writeFileHandle2, posix.fileno( io.stdout ) );
posix.execp( command, commandArguments );
os.exit( 2 );
end
posix.close( writeFileHandle2 );
posix.close( readFileHandle1 );
local dataFromProcess = posix.read( readFileHandle2, MAXIMUM_BYTE_READ_COUNT );
posix.close( readFileHandle2 );
posix.wait( childProcessId2 );
posix.wait( childProcessId1 );
return dataFromProcess;
end
-- 正在执行的命令
-- echo -n AAAAAAAAAAAAAAAA | openssl aes-128-cbc -e -nopad -a -K 30313233343536373839616263646566 -iv 1FF1ECB9000000000000000000000000
-- 期望的结果
-- 28iudIC31lHfDDxfa1/g9w==
result = openReadWritePipe("AAAAAAAAAAAAAAAA","openssl",{"aes-128-cbc", "-e", "-nopad", "-a", "-K", "30313233343536373839616263646566", "-iv", "1FF1ECB9000000000000000000000000"});
print("Result: "..result);
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
Lua 标准库中没有任何东西可以实现这个。
这里有一个深入探讨如何正确进行双向通信的链接,以及一个提出解决方案的例子:
您可能会对这个扩展感兴趣,该扩展添加了在
os
和io
命名空间中实现与子进程双向通信的函数。