I have the following code:
dofile(arg[1])
function1 = arg[2]
argument = arg[3]
returned = _G[function1](argument)
print(returned)
It is designed to take three command-line arguments and run a function from a file.
So, i run the command lua libs.lua "printStuff.lua" "printStuff" "\"Hello, World\"", and i always end up with this:
"Hello, World"
nil
I don't understand why i always get "nil".
Here are the contents of printstuff.lua:
function printStuff(stuff)
print(stuff)
end
That is to be expected. What's going on here:
You're executing the file specified by the first argument, printstuff.lua, which will leave a function printStuff in the global table _G.
You're indexing the global table with the second argument, printStuff, obtaining that function
You're calling the function you just obtained with the third command line argument, "Hello World!", as parameter, which prints it, and storing the result of that in the global variable returned. The function printStuff doesn't return anything (there's no return in there, and even if there was, print doesn't return anything either), so you're assigning nil to returned.
You're printing returned, which is nil
Side note: I'd use the vararg ... instead of the arg table for improved readability:
local file, func, param = ...
dofile(file); print(func(param))
Why not simply...
-- xlua.lua
-- Example: lua xlua.lua os date "%H:%M:%S"
-- Or: lua xlua.lua _G print "Hello World"
-- Or: lua xlua.lua dofile /path/to/someusefull.lua "Arg for someusefull.lua"
local result = _G[arg[1]][arg[2]](arg[3])
-- 5. Only put out whats not nil
if (result ~= nil) then
print(result)
end
Related
I'm working on a lua obfuscator, and I want it to be able to switch from obfuscating code in the file if there are no arguments, or obfuscating the string argument from the lua cli. If that made absolutely no sense here's what I mean.
local Code = function()
print("test")
end
Dumped = string.dump(Code)
if arg[1] then
local function Temp()
loadstring(arg[1])()
end
Dumped = string.dump(Temp)
end
The problem with this is that in the dump, it's not actually whatever the argument is, rather the variable. So how'd I get around that and change my string to code? I'm sorry if I make no sense.
Here's what I've tried and the problem:
local Code = function()
print("test")
end
Dumped = string.dump(Code)
if arg[1] then
local function Temp()
loadstring(arg[1])()
end
Dumped = string.dump(Temp)
end
print(Dumped)
-- Say if we did lua file.lua print("hello world")
-- Expected result: LuaQ print hello world (with a bunch of whitespace in between)
-- Actual result: LuaQ arg[1] (also with a bunch of whitespace in between)
Right now you are dumping a function that loads the string and runs it. So you are getting the dump of the code that loads a string and runs it. No surprise there.
If you want to dump some other code, then you need to actually dump that code:
local code, codeError = loadstring(arg[1])
if code == nil then
print("Error:",codeError)
else
Dumped = string.dump(code)
end
So i'm making my own OS for LIKO-12 and I need to run .lk12 files but i need to have a function to run a string as code and return a table that has all of the variables that code created
Example:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
should return a table with 2 functions:
A function called foo that returns "Hello,"
A function called bar that returns " World!"
and a variable called hello that has "Hello, World!"
You should be able to call a function from the code using
vars["foo"]() or vars.foo()
Can anyone help me?
When you create or use a global variable it's actually stored in a table called "the environment", or _ENV (which is not global, it's a local that you get automatically)
So your code:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
is really doing:
function _ENV.foo()
return "Hello,"
end
function _ENV.bar()
return " World!"
end
_ENV.hello = _ENV.foo().._ENV.bar()
so if we just set _ENV to a new table, and return it, then this is exactly the table you want. You can do this with load (which is how you run strings) by passing it as the 4th argument. This function will do that:
function loadAndReturnEnv(codeString)
local env = {}
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
return env
end
However, note that all the usual global stuff like string.rep, table.sort, load, print don't exist in this new _ENV. If that's what you want, that's great. But I guess you probably do want them. In that case we can use the metatable __index feature, so that if the code looks for something in its _ENV and it's not there, it looks in our (the caller's) _ENV instead.
function loadAndReturnEnvWithGlobals(codeString)
-- note: env is the environment for the code we're about to run
-- (which will be called _ENV in that code), and _ENV is the environment
-- of the loadAndReturnEnvWithGlobals function (i.e. the "real" environment)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
setmetatable(env, nil) -- set the table back to normal before we return it
return env
end
But wait... the program can now call print when you run it, but if it defines a function, that function can't call print since we deleted the link back to the real environment after the code returned. So I think the best way to fix that is to leave the environment linked, and just copy the functions etc.
function loadAndReturnEnvWithGlobals2(codeString)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
local result = {}
for k,v in pairs(env) do result[k]=v end
return result
end
although this copies the values, so if you have any functions that change the variables, you won't see the changed variables.
Which version you use is up to you. They each have pros and cons.
I am writing a Lua 5.3 program and it requires arguments to be passed to it. I use the arg table to get the first argument: arg[1], but the 1st argument, according to the script, is nil even though I have passed an argument to the file.
Here's the code that I've written:
local strin = arg[1]:sub(2,arg[1]:len()-1) -- to remove the quote marks
local i = 0
for W in strin:gmatch(".") do
i = i + 1
if W == " " or W == "\t" then strin = strin:sub(i+1) else break end
end
print(strin)
I pass the argument to the file like this:
C:\Users\WhiteKid\Documents\Scripts>RemoveWhiteSpace.lua " hello world!"
It thinks arg[1] is a nil value when it is not. Is there a different way of getting the arguments passed to a lua script in Lua 5.3?
Since you are calling the .lua script directly (C:\Users\WhiteKid\Documents\Scripts>RemoveWhiteSpace.lua " hello world!"), you seem to have an association with a lua interpreter. You need to make sure you pass %1 or %* to the interpreter you are calling in that association. Alternatively, try calling the Lua interpreter and passing the script name and the parameters and it should work as you expect.
Also, you should check if arg[1] is present and check if the quotes are also there (as they may be removed before the parameters get to the script, so you should not always expect them).
I'm trying to put together a lua testing framework that lets you know the function that had the problem, but when I switched from loadstring to _G, (I switched so my test harness could see the results of the function call) my functions started using 'nil' for the function name
Why can _G not detect the name of the current function in the following code? Also, how can I get the return results from loadstring (ie the 'false' from the blah call) or set the function name when using _G (ie. Tell the lua interpreter what the function name should be)?
function run_test(one, two)
if one ~= two then
print(debug.getinfo(2).name..' Failed')
end
end
function blah()
run_test(false, true)
return false
end
local fname = 'blah'
local status, result = pcall(_G[fname]) -- Outputs 'nil'; result is 'false'
local status, result = pcall(loadstring(fname..'()')) -- Outputs 'blah', result is 'nil'
The main thing I need is a way to call a function using a string of the function name, be able to see the function name inside the call (for test failures to point to the function that failed, like fname = 'blah' in the code above) and be able to get the return value
local fname = 'blah'
status, result = pcall(??Call fname somehow??)
assert(status)
assert(not result)
--stdout should be "blah Failed"
This is a limitation of the heuristics used by Lua to provide names for functions.
In Lua, all functions are anonymous. A given function can be the value of several variables: global, local, and table fields. The Lua debug system tries to find a reasonable name for a value based on where it came from by looking into the bytecode being executed.
Consider the simpler example
blah()
pcall(blah)
In the first call, the debug system sees that the function being called comes from the global blah and debug.getinfo(1).name gives the expected result, blah.
In the second call, the debug system sees that the function being called comes from the first argument to pcall but it does not look further to see where that argument came from, and debug.getinfo(1).name gives nil.
The same thing happens when you call _G[name](). All the debug system sees is a field of a table and the name of the field is too far off.
Try adding print(debug.traceback()) as the first line of blah to see another take on this explanation.
function OnChat(PlayerId, Type, Message)
if Message == "!usecarepackage" then
if ReadKS1 == "CP" or ReadKS2 == "CP" or ReadKS3 == "CP" then
InputConsole("msg %s has requested a care package!", Get_Player_Name_By_ID(pID))
local pos = Get_Position(Get_GameObj(pID))
pos:AssignY(pos:GetY()+1)
building = Create_Object("SignalFlare_Gold_Phys3", pos)
Attach_Script(building, "Test_Cinematic", "caredrop.txt")
if building == nil then
InputConsole("ppage %d Cannot spawn Care Package here.", pID)
elseif math.random(50, 64) == 50 then
Attach_Script(building, "z_Set_Team", Get_Team(pID))
Attach_Script(building, "M00_No_Falling_Damage_DME")
--Add in rest of numbers and corresponding streak, such as UAV if math.random = 50
end
elseif ReadKS1 ~= "CP" or ReadKS2 ~= "CP" or ReadKS3 ~= "CP" then
InputConsole("ppage %d You are not allowed to use that with your current streak selection", pID)
end
end
return 1
end
I know this is scruffy code, but I'm receiving a "Bad argument #2 to 'format' (number expected, got no value)". This relates to this piece of code which prefixes all other pieces:
function InputConsole(...)
Console_Input(string.format(unpack(arg)))
end
Finally, this is for the game Command and Conquer Renegade (should you wish to look up API etc). Any help in what I'm doing wrong would be much appreciated.
The likely problem with this code is the use of the deprecated arg feature in your function InputConsole(). Lua 5.0 used arg as a way to access the actual arguments to a function declared as variadic using the ... token in the argument list.
Edit: But likely doesn't mean correct.
The actual problem looks like the switch in idiom from PlayerId to pID. The former is the named parameter of the function OnChat(), while the latter is a global variable used in the function body without further initialization. Uninitialized globals are nil, so nil is passed to InputConsole() and the error message is telling you the truth.
Old answer that didn't solve it
Lua 5.1 deprecated that usage, and Lua 5.2 removed it entirely.
I'm not sure from the code fragments provided which version of Lua is actually used in this game, but the symptom is consistent with missing the automatically generated arg table.
I would write the function like this:
function InputConsole(...)
Console_Input(string.format(...))
end
But you could also add local arg = {...} as the first line of the function and get much the same effect as provided by Lua 5.0 at the expense of creating (and discarding) the temporary table. The differences are subtle, and mostly have to do with the treatment of nil in tables.
For clarity, I would prefer name the first argument, as it is not really optional.
function InputConsole(fmt, ...)
Console_Input(string.format(fmt, ...))
end
And if you can count on that argument being a string, that could be simplified further to
function InputConsole(fmt,...)
Console_Input(fmt:format(...))
end
If its stringiness is of concern, just say fmt = tostring(fmt) or possibly assert(type(fmt)=="string") before the call to Console_Input().