&引用;“主要”;Lua中的函数?

&引用;“主要”;Lua中的函数?,lua,Lua,在python中,通常会定义一个主函数,以便将脚本用作模块(如果需要): 在Lua中,习惯用法if\uuuuu name\uuuuu==“\uuuu main\uuuuuu”本身是不可能的(这意味着,我认为不可能) 为了在Lua中有类似的行为,我通常会这么做: os.exit((function(args) print("Hello world") return 0 end)(arg)) 。。。但这种方法似乎“过于夸张”:-) 是否有更常见的方法(除了定义一个全局主函数,这似乎

在python中,通常会定义一个主函数,以便将脚本用作模块(如果需要):

在Lua中,习惯用法
if\uuuuu name\uuuuu==“\uuuu main\uuuuuu”
本身是不可能的(这意味着,我认为不可能)

为了在Lua中有类似的行为,我通常会这么做:

os.exit((function(args)
    print("Hello world")
    return 0
end)(arg))
。。。但这种方法似乎“过于夸张”:-)


是否有更常见的方法(除了定义一个全局主函数,这似乎是多余的)?

您可以尝试检查是否需要该模块

从文件:

package.loaded由require使用的表 要控制哪些模块已安装,请执行以下操作: 加载。当您需要模块时 modname和package.loaded[modname]为 不为false,require只返回 存储在那里的值

有了这个,你可以写:

if not package.loaded['modulename'] then
    main()
end
没有“适当”的方法来做到这一点,因为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(stock Lua解释器中文件的正常深度)来工作。但是,此测试可能会在Lua版本之间中断,甚至在任何嵌入式/自定义Lua构建中也会中断

我还将包括这个(稍微更便携的)替代方案,尽管它在启发式方面有了更大的飞跃,并且有一个失败案例(见下文):

这个函数通过比较_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
s一个模块时,它会以varargs(
)的形式将它的名称传递给它

因此,如果您的脚本不打算接受任何参数(从命令行或其他方式),您可以使用

if ... then
  return this_mod --module case
else
  main() --main case
end
但是,请注意,在(完全)可能的情况下,这并不是万无一失的。然而,在这一点上,你可以结合卢卡斯的答案得到:

if not package.loaded[...] then
  --main case
else --module case
end
仍然不完美(例如,如果使用
string
的第一个参数调用脚本,或者使用其他已加载模块的名称),但可能已经足够好了。在其他情况下,我遵从MattJ的回答

if arg ~= nil and arg[0] == string.sub(debug.getinfo(1,'S').source,2) then
  print "Main file"
else
  print "Library"
end
解释:

  • Lua使用
    arg
    表从解释器调用脚本,其中
    arg[0]
    是脚本的名称
  • debug.getinfo
    函数返回一个信息表,该表描述了由编号参数给定的堆栈级别上的函数(1表示调用
    getinfo
    的函数;0表示
    getinfo
    本身)。它的第二个参数是可选的:它指定要检索的getinfo字段的子集。“S”定义源名称
  • 文件的
    getinfo
    返回的表的
    source
    字段包含函数在其中定义的文件名,前缀为
    @
    。通过将该字段的值传递给
    string.sub(…,2)
    ,我们可以剥离该
    @
    。(
    short\u src
    字段包含不带
    @
    的名称,但这是因为它被定义为“打印友好”名称,并且比剥离
    源代码更不安全)

  • 请注意,此代码使用了一个
    debug
    函数,因此在5.2中,您必须
    要求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()函数处理调试库

    if debug.getinfo(1).what == "main" then
        -- Main execution
    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\u main
    ,您可以执行以下操作:

    if debug.getinfo(3) == nil then
        print("Main chunk!")
    else
        print("Library chunk!")
    end
    

    我正在使用Lua5.3,这里的大多数建议都有问题。这就是我的用例的工作原理:

    local my_module = {}
    ...
    if os.getenv('CLI') then
      main()
    else
      return my_module
    end
    
    从命令行运行时,我简单地定义了环境变量,例如:

    CLI=1 lua my_script.lua
    

    为我工作™

    缺点是,您必须知道模块的名称
    arg[0]
    也不起作用,因为lua模块在
    之后匹配。lua;?。BIN
    (BIN是本机库的文件扩展名,例如.so或.dll)您试图实现什么?我假设您希望将此代码放入自己的模块中,并知道如何命名。顺便说一句,arg[0]返回正在运行或需要您的模块的lua文件的名称。try:test1.lua>require“test”test.lua>print(arg[0])“顺便说一句,arg[0]返回正在运行的lua文件的名称”-我在前面的评论中已经提到过…这在lua5.2中不起作用。只有当模块返回时,
    package.loaded
    条目才会被插入。我希望我可以多次向上投票:-)。我想我现在会坚持使用
    getfenv
    方法,因为它看起来更小。我一定会记住另一个解决方案。@coldfix我添加了一些似乎适用于5.2的修改代码。不过,我的其余答案仍然有效,使用风险自负:)这看起来不错,而且比第二种方法更干净/更不容易出错。相应的范围是否保证至少有一个条目?我发布了一个非常类似的解决方案,使用
    debug.getinfo
    而不是
    debug.getlocal
    ,它使用了
    pcall
    
    local my_module = {}
    ...
    if os.getenv('CLI') then
      main()
    else
      return my_module
    end
    
    CLI=1 lua my_script.lua