Подтвердить что ты не робот

"главная" функция в Lua?

В python обычно можно определить основную функцию, чтобы позволить script использоваться в качестве модуля (если необходимо):

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))

... Но этот подход кажется скорее "тяжелым в круглых скобках": -)

Существует ли более общий подход (помимо определения глобальной основной функции, которая кажется избыточной)?

4b9b3361

Ответ 1

Нет никакого "правильного" способа сделать это, поскольку Lua действительно не отличает код от того, откуда он пришел, все они просто являются функциями. Тем не менее, это, по крайней мере, похоже, работает в Lua 5.1:

[email protected]:~$ cat hybrid.lua 
if pcall(getfenv, 4) then
    print("Library")
else
    print("Main file")
end
[email protected]:~$ lua hybrid.lua 
Main file
[email protected]:~$ lua -lhybrid
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
[email protected]:~$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid"
Library
> ^C
[email protected]:~$

Он работает, проверяя, превышает ли глубина стека 3 (нормальная глубина для файла в интерпретаторе Lua на складе). Этот тест может нарушаться между версиями Lua и даже в любых встроенных/настраиваемых сборках Lua.

Я также включу эту (чуть более портативную) альтернативу, хотя она делает еще больший прыжок в эвристике и имеет случай сбоя (см. ниже):

[email protected]:~$ 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
[email protected]:~$ lua hybrid2.lua 
Main file
[email protected]:~$ lua -lhybrid2
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
[email protected]:~$ lua 
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid2"
Library
>

Это работает, сравнивая содержимое _G.arg с содержимым "...". В основной части они всегда будут одинаковыми. В модуле _G.arg по-прежнему будут содержать аргументы командной строки, но "..." будет содержать имя модуля, переданное require(). Я подозреваю, что это ближе к лучшему решению для вас, учитывая, что вы знаете свое имя модуля. Ошибка в этом коде заключается в том, что пользователь выполняет основной script с 1 аргументом, и это точное имя вашего модуля:

[email protected]:~$ lua -i hybrid2.lua hybrid2
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
Main file
> require "hybrid2"
Main file
> 

Учитывая вышеизложенное, я надеюсь, что, по крайней мере, вы знаете, где вы стоите, даже если это не совсем то, что вы имели в виду:)

Обновление:. Для версии hybrid.lua, которая работает в Lua 5.1 и 5.2, вы можете заменить getfenv на debug.getlocal:

if pcall(debug.getlocal, 4, 1) then
    print("Library")
else
    print("Main file")
end

Ответ 2

вы можете попробовать проверить, требуется ли модуль.

из документации:

package.loaded Таблица, используемая требованием контролировать, какие модули уже загружен. Когда вам нужен модуль modname и package.loaded [имя_модема] а не false, требуется просто вернуть сохраненное там значение.

С этим вы можете написать:

if not package.loaded['modulename'] then
    main()
end

Ответ 3

Когда Lua require модуль, он передает ему имя require d с помощью varargs (...).

Итак, если ваш script не намерен принимать какие-либо аргументы (из командной строки или иначе), вы можете использовать что-то вроде

if ... then
  return this_mod --module case
else
  main() --main case
end

Обратите внимание, однако, что это не является безупречным в (полностью) возможном случае, когда вы принимаете аргументы. Однако на данный момент вы можете совместить это с ответом Лукаша, чтобы получить:

if not package.loaded[...] then
  --main case
else --module case
end

Все еще не идеально (например, если script вызывается с первым аргументом string или именем какого-либо другого уже загруженного модуля), но, вероятно, достаточно хорош. В других ситуациях я откладываю ответ MattJ.

Ответ 4

Что не так с этим:

$ 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

Я собираюсь предложить еще один вариант, который, похоже, работает на 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

Ответ 6

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

Объяснение:

  • Lua вызывает script из интерпретатора с таблицей arg, причем arg[0] является именем script.
  • Функция debug.getinfo возвращает таблицу информации, описывающую функцию на уровне стека, заданную ее аргументом с номером (1 относится к функции, вызывающей getinfo; 0 относится к getinfo). Его второй параметр не является обязательным: он определяет, какое подмножество полей getinfo извлекается. "S" определяет имена S ource.
  • Поле source таблицы, возвращаемой getinfo для файлов, содержит имя файла, в котором была определена функция, с префиксом @. Передав значение этого поля в string.sub(...,2), мы разделим его на @. (Поле short_src содержит имя без @, но это потому, что оно определено как "удобное для печати" имя и менее безопасно, чем удаление source.)

Обратите внимание, что этот код использует функцию debug, поэтому в 5.2 вам нужно будет require debug, чтобы иметь возможность использовать его.

Ответ 7

Возможно, вы можете иметь дело с библиотекой отладки с помощью функции debug.getinfo()

if debug.getinfo(1).what == "main" then
    -- Main execution
end

Для получения дополнительной информации см. справочное руководство.

Ответ 8

Я использую lua 5.3 и имел проблемы с большинством предложений здесь. Это то, что сработало для моего использования:

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

При запуске из командной строки я просто определил переменную среды, например:

CLI=1 lua my_script.lua

Работает для меня ™