在python中,通常会定义一个main函数,以便允许脚本用作模块(如果需要):
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))
......但这种做法在括号上看起来相当“:” - )
是否有更常见的方法(除了定义一个似乎多余的全局主函数)?
答案 0 :(得分:13)
没有“正确”的方法来做到这一点,因为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
答案 1 :(得分:6)
当Lua require
是一个模块时,它会以require
d的名称将其作为varargs(...
)传递给它。
因此,如果您的脚本不打算采用任何参数(从命令行或其他方式),您可以使用类似
的内容if ... then
return this_mod --module case
else
main() --main case
end
但是,请注意,在您采用参数的(完全)可能的情况下,这并非万无一失。但是,此时,您可以将此与Lukasz的答案结合起来:
if not package.loaded[...] then
--main case
else --module case
end
仍然不完美(例如,如果使用string
的第一个参数或其他已经加载的模块的名称调用脚本,但可能已经足够好了。在其他情况下,我会按照MattJ的回答。
答案 2 :(得分:5)
您可以尝试检查是否需要该模块。
来自文档:
package.loaded require使用的表 控制哪些模块已经存在 加载。当你需要一个模块 modname和package.loaded [modname]是 不是假的,要求简单地归还 存储在那里的价值。
有了这个,你可以写:
if not package.loaded['modulename'] then
main()
end
答案 3 :(得分:3)
我将建议另一种变体,这似乎适用于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
答案 4 :(得分:2)
这有什么问题:
$ 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
>
答案 5 :(得分:1)
if arg ~= nil and arg[0] == string.sub(debug.getinfo(1,'S').source,2) then
print "Main file"
else
print "Library"
end
解释:
arg
表从解释器调用脚本,其中arg[0]
是脚本的名称。debug.getinfo
函数返回一个信息表,描述由编号参数给出的堆栈级别的函数(1表示调用getinfo
的函数; 0表示getinfo
本身)。它的第二个参数是可选的:它指定要检索的getinfo字段的哪个子集。 'S'定义 S 源名称。source
为文件返回的表的getinfo
字段包含定义函数的文件的名称,前缀为@
。通过将该字段的值传递给string.sub(...,2)
,我们将@
删除。 (short_src
字段包含不带@
的名称,但这是因为它被定义为“打印友好”名称,并且不如剥离source
安全。)请注意,此代码使用debug
函数,因此在5.2中您必须require debug
才能使用它。
答案 6 :(得分:1)
也许您可以使用 debug.getinfo()函数来处理调试库
if debug.getinfo(1).what == "main" then
-- Main execution
end
有关详细信息,请参阅参考手册。
答案 7 :(得分:0)
我正在使用lua 5.3并且在这里遇到了大多数建议的问题。这对我的用例有用:
local my_module = {}
...
if os.getenv('CLI') then
main()
else
return my_module
end
从命令行运行时,我简单地定义了环境变量,例如:
CLI=1 lua my_script.lua
为我工作™