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

点赞
stackoverflow用户459706
stackoverflow用户459706

你可以尝试检查模块是否已被引入。

从文档中得知:

package.loaded 是由 require 函数用来控制哪些模块已经被加载的表。当你要引入一个名为 modname 的模块且 package.loaded[modname] 不为假值时,require 函数将直接返回存储在该位置的值。

因此,你可以这样编写代码:

if not package.loaded['modulename'] then
    main()
end
2010-12-23 17:41:34
stackoverflow用户15996
stackoverflow用户15996

没有“正确”的方法来做到这一点,因为 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
2010-12-23 19:47:50
stackoverflow用户34799
stackoverflow用户34799

当 Lua require 一个模块时,它会将模块的名字作为可选参数 (...) 传递给它。

因此,如果你的脚本不想从命令行或其他途径接受任何参数,你可以使用如下代码:

if ... then
  return this_mod --模块情况
else
  main() --主程序情况
end

然而需要注意的是,这并不能完全保证在有参数的情况下代码的正确性。但是,你可以使用 Lukasz 提供的方法结合此处代码:

if not package.loaded[...] then
  --主程序情况
else --模块情况
end

虽然这种方法不完美(例如,如果脚本的第一个参数为 string 或其他已加载的模块的名称),但足够好用。在其他情况下,我会使用 MattJ 提供的方法。

2011-05-16 22:12:32
stackoverflow用户34799
stackoverflow用户34799
如果 arg 不为 nilarg[0] 等于 string.sub(debug.getinfo(1,'S').source,2),则
  打印 "Main file"
否则
  打印 "Library"

解释:

  1. Lua 通过 arg 表格从解释器调用脚本,其中 arg[0] 是脚本的名称。
  2. debug.getinfo 函数返回一个描述给定从栈顶起第几层的函数的信息的表格(1 表示调用 getinfo 的函数,0 表示 getinfo 函数本身)。它的第二个参数是可选的:它指定要检索哪些 getinfo 字段的子集。 'S' 定义了“Source names”。
  3. 对于文件,getinfo 返回的表格中的 source 字段包含定义函数的文件的名称,并在前面加上 @。通过将该字段的值传递给 string.sub(...,2),我们删除了该 @。(short_src 字段包含没有 @ 的名称,但这是因为它被定义为“print-friendly”的名称,并且比删除 source 更不安全。)

请注意,此代码使用 debug 函数,因此在 5.2 版本中,您需要 require debug 才能使用它。

2011-05-16 22:34:25
stackoverflow用户1218580
stackoverflow用户1218580

下面代码有什么问题:

$ 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
>
2012-02-18 23:39:15
stackoverflow用户1211342
stackoverflow用户1211342

也许你只需要使用调试库,并使用 debug.getinfo() 函数

如果 debug.getinfo(1).what == "main" then
    -- 主要执行
end

更多信息请参考参考手册。

2012-02-19 22:05:34
stackoverflow用户650222
stackoverflow用户650222

我建议另一种变化,它似乎在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
2013-09-11 18:00:26
stackoverflow用户1303982
stackoverflow用户1303982

我正在使用 Lua 5.3 版本,并且使用这里的大部分建议都出现了问题。下面是在我的使用场景下有效的方法:

local my_module = {}
...
if os.getenv('CLI') then
  main()
else 
  return my_module
end

当从命令行运行时,只需定义环境变量如下即可:

CLI=1 lua my_script.lua

对我来说有效™。

2016-08-05 00:32:16