Lua中的“main”函数?
在 Python 中,通常会定义一个主函数,以便在需要时将脚本用作模块:
def main():
print("Hello world")
return 0
if __name__ == "__main__":
sys.exit(main())
在 Lua 中,习惯用语 if __name__ == "__main__"
并不可能(也就是说,我认为不可能)。
这就是我通常做的,以便在 Lua 中具有类似的行为:
os.exit((function(args)
print("Hello world")
return 0
end)(arg))
......但这种方法似乎有点“括号重”:-)
除了定义全局主函数(似乎多余)之外,还有更常见的方法吗?
原文链接 https://stackoverflow.com/questions/4521085
没有“正确”的方法来做到这一点,因为 Lua 实际上并不根据代码来区分它来自哪里,它们都只是函数。尽管如此,这种方法至少似乎在 Lua 5.1 中可以工作:
matthew@silver:~$ cat hybrid.lua
if pcall(getfenv, 4) then
print("Library")
else
print("Main file")
end
matthew@silver:~$ lua hybrid.lua
Main file
matthew@silver:~$ lua -lhybrid
Library
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid"
Library
> ^C
matthew@silver:~$
它的工作原理是检查堆栈深度是否大于 3(股票 Lua 解释器中文件的正常深度)。这个测试可能会在不同版本的 Lua 中出现问题,甚至在任何嵌入式/自定义 Lua 构建中也可能会出现问题。
我还将包括这个(稍微更易移植的)替代方法,尽管它在启发式方面跨越了更大的距离,并且有一个失败的案例(请参阅下面):
matthew@silver:~$ cat hybrid2.lua
function is_main(_arg, ...)
local n_arg = _arg and #_arg or 0;
if n_arg == select("#", ...) then
for i=1,n_arg do
if _arg[i] ~= select(i, ...) then
print(_arg[i], "does not match", (select(i, ...)))
return false;
end
end
return true;
end
return false;
end
if is_main(arg, ...) then
print("Main file");
else
print("Library");
end
matthew@silver:~$ lua hybrid2.lua
Main file
matthew@silver:~$ lua -lhybrid2
Library
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid2"
Library
>
这个函数是通过将 _G.arg 的内容与“...”的内容进行比较来工作的。在主块中,它们将始终相同。在模块中,_G.arg 仍将包含命令行参数,但“...”将包含传递给 require() 的模块名称。我怀疑这更接近于您的更好的解决方案,因为您知道您的模块名称。这个代码中的错误在于当用户使用 1 个参数执行主脚本,并且这正是您的模块的确切名称时:
matthew@silver:~$ lua -i hybrid2.lua hybrid2
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
Main file
> require "hybrid2"
Main file
>
鉴于上述情况,我希望至少您知道自己处于什么地步,即使这不完全是您想要的 :)
更新: 为了使适用于 Lua 5.1 和 5.2 的 hybrid.lua 版本,可以使用 debug.getlocal 替换 getfenv:
if pcall(debug.getlocal, 4, 1) then
print("Library")
else
print("Main file")
end
当 Lua require
一个模块时,它会将模块的名字作为可选参数 (...
) 传递给它。
因此,如果你的脚本不想从命令行或其他途径接受任何参数,你可以使用如下代码:
if ... then
return this_mod --模块情况
else
main() --主程序情况
end
然而需要注意的是,这并不能完全保证在有参数的情况下代码的正确性。但是,你可以使用 Lukasz 提供的方法结合此处代码:
if not package.loaded[...] then
--主程序情况
else --模块情况
end
虽然这种方法不完美(例如,如果脚本的第一个参数为 string
或其他已加载的模块的名称),但足够好用。在其他情况下,我会使用 MattJ 提供的方法。
如果 arg 不为 nil 且 arg[0] 等于 string.sub(debug.getinfo(1,'S').source,2),则
打印 "Main file"
否则
打印 "Library"
解释:
- Lua 通过
arg
表格从解释器调用脚本,其中arg[0]
是脚本的名称。 debug.getinfo
函数返回一个描述给定从栈顶起第几层的函数的信息的表格(1 表示调用getinfo
的函数,0 表示getinfo
函数本身)。它的第二个参数是可选的:它指定要检索哪些 getinfo 字段的子集。 'S' 定义了“Source names”。- 对于文件,
getinfo
返回的表格中的source
字段包含定义函数的文件的名称,并在前面加上@
。通过将该字段的值传递给string.sub(...,2)
,我们删除了该@
。(short_src
字段包含没有@
的名称,但这是因为它被定义为“print-friendly”的名称,并且比删除source
更不安全。)
请注意,此代码使用 debug
函数,因此在 5.2 版本中,您需要 require debug
才能使用它。
下面代码有什么问题:
$ cat aa.lua
#!/usr/bin/lua
if (arg ~= nil and arg[-1] ~= nil) then
print "main"
else
print "library"
end
$ ./aa.lua
main
$ ./aa.lua arg1 arg2
main
$ cat bb.lua
#!/usr/bin/lua
print("in bb")
$ lua -laa bb.lua
library
in bb
$ lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "aa"
library
>
也许你只需要使用调试库,并使用 debug.getinfo() 函数
如果 debug.getinfo(1).what == "main" then
-- 主要执行
end
更多信息请参考参考手册。
我建议另一种变化,它似乎在lua5.1和lua5.2上都可以工作:
function is_main(offset)
return debug.getinfo(4 + (offset or 0)) == nil
end
if is_main() then
print("Main chunk!")
else
print("Library chunk!")
end
如果你不想为此定义额外的函数is_main
,你可以这样做:
if debug.getinfo(3) == nil then
print("Main chunk!")
else
print("Library chunk!")
end
我正在使用 Lua 5.3 版本,并且使用这里的大部分建议都出现了问题。下面是在我的使用场景下有效的方法:
local my_module = {}
...
if os.getenv('CLI') then
main()
else
return my_module
end
当从命令行运行时,只需定义环境变量如下即可:
CLI=1 lua my_script.lua
对我来说有效™。
- 如何在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 模式将字符串(嵌套数组)转换为真正的数组?
你可以尝试检查模块是否已被引入。
从文档中得知:
因此,你可以这样编写代码:
if not package.loaded['modulename'] then main() end