在为我的 LuaTeX 代码编写一些 Lua 作为后端时,我注意到以下内容。作为背景,这里是 Lua 代码。这个版本是标准Lua。但你并不需要真正了解 Lua 才能理解该函数在做什么。
我需要一个函数,它可以将标准输出和错误返回给 Lua,从而返回给 TeX。
由于 Lua 没有执行此操作的库函数,因此我必须自己编写一个。
此版本将标准输出写入文件,并将标准错误重定向到标准输出。然后它读取它们并返回它们。这不是特别漂亮,但更好的替代方案并不明显。
local function exec_cmd(command, stdout_message, stderr_message)
local filename=os.tmpname()
local cmd = string.format(command .. " 2>&1 1> %s", filename)
local pipe = io.popen(cmd)
local stderr = pipe:read("*all")
pipe:close()
local f = assert(io.open(filename, "r"))
local stdout = f:read("*all")
f:close()
os.remove (filename)
if stdout ~= nil and stdout ~= '' then
print(string.format(stdout_message, stdout))
end
if stderr ~= nil and stderr ~= '' then
error(string.format(stderr_message, stderr))
end
end
今天,在一时的困惑中,我编写了以下代码作为command
上面函数的参数。
"wkhtmltopdf searchpath(foo.html) foo.pdf"
就上面给出的 Lua 函数而言,这将类似于
exec_cmd("wkhtmltopdf searchpath(foo.html) foo.pdf", "STDOUT FROM WKHTMLTOPDF is %s", "STDERR FROM WKHTMLTOPDF IS %s")
所以这个命令被传递到 shell:
wkhtmltopdf searchpath(foo.html) foo.pdf 2>&1 1> /tmp/lua_vyOiay
对于一些合适的临时文件,例如/tmp/lua_vyOiay
.
如果直接在 shell 上输入(并且忘记了 Lua,它已经达到了解释的目的),这会给出错误
bash:意外标记“(”附近出现语法错误
但有趣的是,这个错误似乎没有进入标准输出。至少,上面的命令没有收集它,而且我无法以任何其他方式收集它。
因此,为了保证我的 Lua 函数的安全,以下是我的问题。
- 上面那个无意义的命令发生了什么,为什么错误没有被发送到 stderr?这仅供我参考。
- 我可以做什么(如果有的话)来确保收集 stderr 字符串,并中断我的 TeX 程序,因为它应该被中断?
经过进一步讨论,2至少可以通过2种方式来完成。
2a.由于 Bash shell 本身似乎会给出该错误,因此我需要一种方法来捕获 Bash shell 本身的标准错误。
2b.获取 shell 标准错误的 Lua 方法,这里是由 Lua 生成的。
答案1
Lua程序基本上可以归结为运行
io.popen("syntax(error) 2>&1 1>outputfile"):read("*all")
其运行相当于
sh -c 'syntax(error) 2>&1 1>outputfile'
io.popen()
捕获它启动的 shell 的标准输出,但不捕获标准错误。如果那里没有语法错误,重定向会将 stderr 安排到管道,将 stdout 安排到文件。但由于语法错误,shell 无法执行到那么远,因此不会处理重定向。 (外壳程序不明白您要告诉它做什么。)
要实际捕获 stdout 和 stderr,通常会在启动外部命令之前将两者连接到管道(或其他)。例如,Pythonsubprocess.Popen()
有stdout
和stderr
参数。但是Lua的标准库有点贫瘠,而且看起来并不是可以直接使用的。您必须编写一个 C 模块来提供该功能。
正如评论中提到的,将命令包装起来eval
会有所帮助,因为生成的命令行将是
eval 'syntax(error)' 2>&1 1>outputfile
shell 会在运行之前处理重定向eval
。但是,当然,您需要添加一组额外的转义来保护内部命令中可能有的任何单引号。