"главная" функция в 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))
... Но этот подход кажется скорее "тяжелым в круглых скобках": -)
Существует ли более общий подход (помимо определения глобальной основной функции, которая кажется избыточной)?
Ответы
Ответ 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
Работает для меня ™