Lua是一门简洁小巧的脚本语言,由C语言编写,一般用来做扩展程序,嵌入在Nginx/Redis等中间件或者其它牛逼语言中使用。
要开发调试Lua程序,最低配的玩法就是文本编辑器+Lua解释器/编译器。
https://code.google.com/archive/p/luaforwindows
wget http://www.lua.org/ftp/lua-5.2.3.tar.gz
tar zxf lua-5.2.3.tar.gz
cd lua-5.2.3
make linux test
curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz
tar zxf lua-5.2.3.tar.gz
cd lua-5.2.3
make macosx test
或者使用brew安装:
brew install lua
注释以"–"开头,如:
--[[ my first program in Lua --]]
标识符用于标记变量、函数或者其它用户定义的项目。合法的标识符包含字母、下划线以及数字,其中数字不能位于首字符。例如:
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal
local d , f = 5 ,10 --declaration of d and f as local variables.
d , f = 5, 10; --declaration of d and f as global variables.
d, f = 10 --[[declaration of d and f as global variables. Here value of f is nil --]]
-- Variable definition:
local a, b
-- Initialization
a = 10
b = 30
print("value of a:", a)
print("value of b:", b)
-- Swapping of variables
b, a = a, b
print("value of a:", a)
print("value of b:", b)
f = 70.0/3.0
print("value of f", f)
print(type("What is my type")) --> string
t = 10
print(type(5.8*t)) --> number
print(type(true)) --> boolean
print(type(print)) --> function
print(type(nil)) --> nil
print(type(type(ABC))) --> string
value = condition and trueval or falseval;
a = 10
while( a < 20 )
do
print("value of a:", a)
a = a+1
end
for i = 10,1,-1
do
print(i)
end
--[ local variable definition --]
a = 10
--[ repeat loop execution --]
repeat
print("value of a:", a)
a = a + 1
until( a > 15 )
while(condition)
do
while(condition)
do
statement(s)
end
statement(s)
end
--[ local variable definition --]
a = 10;
--[ check the boolean condition using if statement --]
if( a < 20 )
then
--[ if condition is true then print the following --]
print("a is less than 20" );
end
print("value of a is :", a);
--[ local variable definition --]
a = 100;
--[ check the boolean condition --]
if( a < 20 )
then
--[ if condition is true then print the following --]
print("a is less than 20" )
else
--[ if condition is false then print the following --]
print("a is not less than 20" )
end
print("value of a is :", a)
--[ local variable definition --]
a = 100
--[ check the boolean condition --]
if( a == 10 )
then
--[ if condition is true then print the following --]
print("Value of a is 10" )
elseif( a == 20 )
then
--[ if else if condition is true --]
print("Value of a is 20" )
elseif( a == 30 )
then
--[ if else if condition is true --]
print("Value of a is 30" )
else
--[ if none of the conditions is true --]
print("None of the values is matching" )
end
print("Exact value of a is: ", a )
--[ local variable definition --]
a = 100;
b = 200;
--[ check the boolean condition --]
if( a == 100 )
then
--[ if condition is true then check the following --]
if( b == 200 )
then
--[ if condition is true then print the following --]
print("Value of a is 100 and b is 200" );
end
end
print("Exact value of a is :", a );
print("Exact value of b is :", b );
optional_function_scope function function_name( argument1, argument2, argument3........,
argumentn)
function_body
return result_params_comma_separated
end
function max(num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
-- calling a function
print("The maximum of the two numbers is ",max(10,4))
print("The maximum of the two numbers is ",max(5,6))
myprint = function(param)
print("This is my print function - ##",param,"##")
end
function add(num1,num2,functionPrint)
result = num1 + num2
functionPrint(result)
end
myprint(10)
add(2,5,myprint)
function average(...)
result = 0
local arg = {...}
for i,v in ipairs(arg) do
result = result + v
end
return result/#arg
end
print("The average is",average(10,5,3,4,5,6))
string1 = "Lua"
print("\"String 1 is\"",string1)
string2 = 'Tutorial'
print("String 2 is",string2)
string3 = [["Lua Tutorial"]]
print("String 3 is",string3)
array = {"Lua", "Tutorial"}
for key,value in ipairs(array)
do
print(key, value)
end
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子是ipairs,它遍历数组的每一个元素。
function square(iteratorMaxCount,currentNumber)
if currentNumber
array = {"Lua", "Tutorial"}
function elementIterator (collection)
local index = 0
local count = #collection
-- The closure function is returned
return function ()
index = index + 1
if index <= count
then
-- return the current element of the iterator
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
-- Simple empty table
mytable = {}
print("Type of mytable is ",type(mytable))
mytable[1]= "Lua"
mytable["wow"] = "Tutorial"
print("mytable Element at index 1 is ", mytable[1])
print("mytable Element at index wow is ", mytable["wow"])
-- alternatetable and mytable refers to same table
alternatetable = mytable
print("alternatetable Element at index 1 is ", alternatetable[1])
print("mytable Element at index wow is ", alternatetable["wow"])
alternatetable["wow"] = "I changed it"
print("mytable Element at index wow is ", mytable["wow"])
-- only variable released and and not table
alternatetable = nil
print("alternatetable is ", alternatetable)
-- mytable is still accessible
print("mytable Element at index wow is ", mytable["wow"])
mytable = nil
print("mytable is ", mytable)
从 Redis 2.6.0 版本开始,通过内置的Lua解释器,可以使用EVAL命令对 Lua 脚本进行求值。命令格式如下:
EVAL script numkeys key [key ...] arg [arg ...]
eg.
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
所有的 Redis 命令,在执行之前都会被分析,籍此来确定命令会对哪些键进行操作。脚本功能被设计成与集群功能兼容,使用正确形式来传递KEY可以确保 Redis 集群可以将你的请求发送到正确的集群节点。
在Lua脚本中可以使用redis.call()和redis.pcall()来执行Redis命令:
eval "return redis.call('set',KEYS[1],'bar')" 1 foo
当 redis.call() 在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因:
redis> lpush foo a
(integer) 1
redis> eval "return redis.call('get', 'foo')" 0
(error) ERR Error running script (call to f_282297a0228f48cd3fc6a55de6316f31422f5d17): ERR Operation against a key holding the wrong kind of value
redis.pcall() 出错时并不引发(raise)错误,而是返回一个带 err 域的 Lua 表(table),用于表示错误
redis 127.0.0.1:6379> EVAL "return redis.pcall('get', 'foo')" 0
(error) ERR Operation against a key holding the wrong kind of value
当 Lua 通过call()或pcall()函数执行 Redis 命令的时候,命令的返回值会被转换成Lua数据结构。同样地,当Lua脚本在Redis内置的解释器里运行时,Lua脚本的返回值也会被转换成Redis协议(protocol),然后由EVAL将值返回给客户端。
为了减少带宽的消耗,Redis实现了EVALSHA命令,它的作用和EVAL一样,都用于对脚本求值,但它接受的第一个参数不是脚本,而是脚本的SHA1校验和(sum)。
如果服务器还记得给定的 SHA1 校验和所指定的脚本,那么执行这个脚本;如果服务器不记得给定的 SHA1 校验和所指定的脚本,那么它返回一个特殊的错误,提醒用户使用EVAL代替EVALSHA。
> set foo bar
OK
> eval "return redis.call('get','foo')" 0
"bar"
> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"
> evalsha ffffffffffffffffffffffffffffffffffffffff 0
(error) `NOSCRIPT` No matching script. Please use [EVAL](/commands/eval).
Redis 保证所有被运行过的脚本都会被永久保存在脚本缓存当中,这意味着,当EVAL命令在一个 Redis 实例上成功执行某个脚本之后,随后针对这个脚本的所有EVALSHA命令都会成功执行。
刷新脚本缓存的唯一办法是显式地调用 SCRIPT FLUSH 命令,这个命令会清空运行过的所有脚本的缓存。
在流水线请求的上下文中使用EVALSHA命令时,要特别小心,因为一旦在流水线中因为EVALSHA命令而发生NOSCRIPT错误,那么这个流水线就再也没有办法重新执行了。客户端可以采用如下措施避免:
Redis 提供了以下几个SCRIPT命令,用于对脚本子系统(scripting subsystem)进行控制:
为了防止不必要的数据泄漏进 Lua 环境, Redis 脚本不允许创建全局变量。如果一个脚本需要在多次执行之间维持某种状态,它应该使用Redis key来进行状态保存。企图在脚本中访问一个全局变量(不论这个变量是否存在)将引起脚本停止,EVAL命令会返回一个错误:
redis 127.0.0.1:6379> eval 'a=10' 0
(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a'
Redis内置的Lua解释器加载了以下Lua库:
脚本应该仅仅用于传递参数和对Redis数据进行处理,它不应该尝试去访问外部系统(比如文件系统),或者执行任何系统调用。
脚本有一个最大执行时间限制,它的默认值是5秒钟,由lua-time-limit选项来控制(以毫秒为单位),可以通过编辑redis.conf文件或者使用CONFIG GET和CONFIG SET命令来修改它。
当脚本运行的时间超过最大执行时间后,以下动作会被执行:
Redis从3.2开始提供了一个完整的Lua调试器,代号为ldb。
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2
逗号前面为key列表,逗号后面为参数列表,注意逗号前后都需要有空格,否则会报错。
进入到调试模式后,Redis只支持如下三个命令:
lua debugger> help
Redis Lua debugger help:
[h]elp Show this help.
[s]tep Run current line and stop again.
[n]ext Alias for step.
[c]continue Run till next breakpoint.
[l]list List source code around current line.
[l]list [line] List source code around [line].
line = 0 means: current position.
[l]list [line] [ctx] In this form [ctx] specifies how many lines
to show before/after [line].
[w]hole List all source code. Alias for 'list 1 1000000'.
[p]rint Show all the local variables.
[p]rint <var> Show the value of the specified variable.
Can also show global vars KEYS and ARGV.
[b]reak Show all breakpoints.
[b]reak <line> Add a breakpoint to the specified line.
[b]reak -<line> Remove breakpoint from the specified line.
[b]reak 0 Remove all breakpoints.
[t]race Show a backtrace.
[e]eval <code> Execute some Lua code (in a different callframe).
[r]edis <cmd> Execute a Redis command.
[m]axlen [len] Trim logged Redis replies and Lua var dumps to len.
Specifying zero as <len> means unlimited.
[a]abort Stop the execution of the script. In sync
mode dataset changes will be retained.
Debugger functions you can call from Lua scripts:
redis.debug() Produce logs in the debugger console.
redis.breakpoint() Stop execution as if there was a breakpoint in the
next line of code.
if counter > 10 then redis.breakpoint() end
在调试中,输入s可以单步执行,使用c可以执行到下一个断点位置。
3. 同步模式
加上–ldb-sync-mode即可进入同步调试模式。
4. 打印变量
在调试命令行中输入p var可以在控制台中打印出变量var的取值。
5. 执行命令
在调试中,还可以输入e执行一些Lua脚本,不过会在另外一个调用帧中执行,因为调试会话是一个forked session。
lua debugger> e redis.sha1hex('foo')
<retval> "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
ZeroBrane是一个免费、开源、跨平台的Lua IDE,可以很方便地开发调试Lua脚本。
打开如下页面下载对应平台的源文件并安装:
https://studio.zerobrane.com/download?not-this-time
下载插件
https://github.com/pkulchenko/ZeroBranePackage/blob/master/redis.lua
然后放置到ZeroBrane安装目录的packages(/opt/zbstudio/packages/)下,或者是~/.zbstudio/packages文件夹中。
打开IDE,新建一个Lua脚本,然后Project -> Lua Interpreter,选择Redis
然后Project -> Command Line Parameters…,输入对应的key和参数,注意逗号前后都要有空格(这里实际上执行的就是Redis的eval命令)。
然后点击运行,ZeroBrane会提示输入redis地址
可以设置断点,查看变量和单步执行
ZeroBrane还提供了Remote Console用于远程执行Lua脚本和Redis命令(全大写或者用@开头的小写Redis命令):