零、文章目录

Nginx14-Lua基础

  • Nginx是可扩展的,可用于处理各种使用场景。本节中,我们将探讨使用Lua扩展Nginx的功能。

1、简介

(1)概念
  • Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵活的扩展和定制功能。
(2)特性
  • 跟其他语言进行比较,Lua有其自身的特点:

    • 轻量级:Lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入到其他程序中。
    • 可扩展:Lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,Lua可以使用它们,就像内置的功能一样。
    • 支持面向过程编程和函数式编程
(3)应用场景
  • 游戏开发
  • 独立应用脚本
  • web应用脚本
  • 扩展和数据库插件
  • 系统安全上

2、Lua的安装

(1)下载地址
(2)安装
  • 在linux上安装Lua非常简单,只需要下载源码包并在终端解压、编译即可使用。
# 创建文件目录
mkdir /opt/lua
cd /opt/lua

# 下载
# 我们可以下载再上传服务器,也可以使用wget命令在服务器上直接下载
wget https://www.lua.org/ftp/lua-5.4.7.tar.gz

# 解压
tar -zxvf lua-5.4.7.tar.gz

# 编译安装
cd lua-5.4.7
make linux test
make install

# 验证是否安装成功
lua -v
  • 如果在执行make linux test失败,报系统缺少libreadline-dev依赖包,需要通过命令来进行安装
yum install -y readline-devel

3、Lua的语法

  • Lua和C/C++语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C++基本一致。如果对C/C++不太熟悉的同学来说,也没关系,因为天下语言是一家,基本上理解起来都不会太困难。我们一点点来讲。
(1)交互方式
  • Lua有两种交互方式

    • **交互式:**指可以在命令行输入程序,然后回车就可以看到运行的效果。
    • **脚本式:**脚本式是将代码保存到一个以 lua 为扩展名的文件中并执行的方式。
  • **交互式:**Lua交互式编程模式可以通过命令lua -i 或lua回车来进入Lua交互式环境,直接输入命令回车就可以看到效果,最后通过Ctrl+C可以退出交互环境。

[root@localhost lua-5.4.7]# lua
Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio
> print("hello world!")
hello world!
> ^C
  • **脚本式1:**将代码保存到一个以lua为扩展名的文件中,用lua hello.lua执行。
# 创建lua文件,文件写入命令
cd /opt/lua/lua-5.4.7/
vim hello.lua
print("hello world!")

# lua执行
[root@localhost lua]# lua hello.lua
hello world!
  • **脚本式2:**直接运行hello.lua文件

    • 将hello.lua做如下修改,第一行用来指定Lua解释器所在位置为 /usr/local/bin/lua,加上#号标记解释器会忽略它。一般情况下#!就是用来指定用哪个程序来运行本文件。
    #!/usr/local/bin/lua
    print("Hello World!!!")
    
    • 但是hello.lua并不是一个可执行文件,需要通过chmod来设置可执行权限如下
    chmod 755 hello.lua
    
    • 然后执行该文件
    [root@localhost lua]# ./hello.lua
    Hello World!!!
    
  • **交互式中运行脚本:**如果想在交互式中运行脚本式的hello.lua中的内容,我们可以使用一个dofile函数

[root@localhost lua]# lua
Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio
> dofile("hello.lua")
Hello World!!!
(2)分隔符
  • 注意:在Lua语言中,连续语句之间的分隔符并不是必须的,也就是说后面不需要加分号,当然加上也不会报错,

  • 在Lua语言中,表达式之间的换行也起不到任何作用。如以下四个写法,其实都是等效的,不建议使用第四种方式,可读性太差。

写法一
a=1
b=a+2
写法二
a=1;
b=a+2;
写法三
a=1; b=a+2;
写法四
a=1 b=a+2
(3)注释
  • 关于Lua的注释要分两种,第一种是单行注释,第二种是多行注释。

  • 单行注释的语法为:

--注释内容
  • 多行注释的语法为:
--[[
	注释内容
	注释内容
--]]
  • 如果想取消多行注释,只需要在第一个–之前在加一个-即可,如:
---[[
	注释内容
	注释内容
--]]
(4)标识符
  • 换句话说标识符就是我们的变量名。
  • Lua定义变量名以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。
  • 这块建议大家最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。
(5)关键字
  • 下列是Lua的关键字,大家在定义常量、变量或其他用户自定义标识符都要避免使用以下这些关键字:
  • 一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。这个也是上面我们不建议这么定义标识符的原因。
(6)运算符
  • Lua中支持的运算符有算术运算符、关系运算符、逻辑运算符、其他运算符。

  • 算术运算符

+   加法
-	减法
*	乘法
/	除法
%	取余
^	乘幂
-	负号

--案例如下
10+20	-->30
20-10	-->10
10*20	-->200
20/10	-->2
3%2		-->1
10^2	-->100
-10		-->-10
  • 关系运算符
==	等于
~=	不等于
>	大于
<	小于
>=	大于等于
<=	小于等于

--案例如下
10==10		-->true
10~=10		-->false
20>10		-->true
20<10		-->false
20>=10		-->true
20<=10		-->false
  • 逻辑运算符
and	逻辑与	 A and B     &&   
or	逻辑或	 A or B     ||
not	逻辑非  取反,如果为true,则返回false  !

--逻辑运算符可以作为if的判断条件,返回的结果如下
A = true
B = true

A and B	-->true
A or  B -->true
not A 	-->false

A = true
B = false

A and B	-->false
A or  B -->true
not A 	-->false

A = false
B = true

A and B	-->false
A or  B -->true
not A 	-->true
  • 其他运算符
..	连接两个字符串
#	一元预算法,返回字符串或表的长度

--案例如下
> "HELLO ".."WORLD"		-->HELLO WORLD
> #"HELLO"			-->5
(7)全局变量
  • 在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil:
[root@master lua-5.4.7]# lua
Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio
> print(b)
nil
> b=100
> print(b)
100
> b=nil
> print(b)
nil
(8)局部变量
  • 要想声明一个局部变量,需要使用local来声明,终端交互式的 local 声明的变量在同一行使用,换行了则离开了该变量的作用域。
[root@master lua-5.4.7]# lua
Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio
> local a=100
> print(a)
nil
> local a=100 print(a)
100

3、Lua数据类型

  • Lua有8个数据类型
nil(空,无效值)
boolean(布尔,true/false)
number(数值)
string(字符串)
function(函数)
table(表)
thread(线程)
userdata(用户数据)
  • 可以使用type函数测试给定变量或者的类型:
print(type(nil))				-->nil
print(type(true))               --> boolean
print(type(1.1*1.1))             --> number
print(type("Hello world"))      --> string
print(type(io.stdin))			-->userdata
print(type(print))              --> function
print(type(type))               -->function
print(type{})					-->table
print(type(type(X)))            --> string
(1)nil
  • nil是一种只有一个nil值的类型,它的作用可以用来与其他所有值进行区分
  • 想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会会释放该变量所占用的内存。
(2)boolean
  • boolean类型具有两个值,true和false。boolean类型一般被用来做条件判断的真与假。

  • 在Lua语言中,只会将false和nil视为假,其他的都视为真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多数语言不太一样。

(3)number
  • 在Lua5.3版本开始,Lua语言为数值格式提供了两种选择integer(整型)和float(双精度浮点型),和其他语言不太一样,float不代表单精度类型。

  • 数值常量的表示方式:

>4			-->4
>0.4		-->0.4
>4.75e-3	-->0.00475
>4.75e3		-->4750
  • 不管是整型还是双精度浮点型,使用type()函数来取其类型,都会返回的是number,所以它们之间是可以相互转换的,同时,具有相同算术值的整型值和浮点型值在Lua语言中是相等的
>type(3)	-->number
>type(3.3)	-->number
(4)string
  • Lua语言中的字符串即可以表示单个字符,也可以表示一整本书籍。在Lua语言中,操作100K或者1M个字母组成的字符串的程序很常见。

  • 可以使用单引号或双引号来声明字符串。

>a = "hello"
>b = 'world'
>print(a)	-->hello
>print(b) 	-->world
  • 如果声明的字符串比较长或者有多行,则可以使用如下方式进行声明
html = [[
<html>
<head>
<title>Lua-string</title>
</head>
<body>
<a href="http://www.lua.org">Lua</a>
</body>
</html>
]]
(5)table
  • table是Lua语言中最主要和强大的数据结构。使用表, Lua 语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。 Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值作索引(除nil外)。

  • 创建表的最简单方式

> a = {}
  • 创建数组
--数组:就是相同数据类型的元素按照一定顺序排列的集合
--创建数组
>arr = {"TOM","JERRY","ROSE"}

--要想获取数组中的值,我们可以通过如下内容来获取
print(arr[0])		nil
print(arr[1])		TOM
print(arr[2])		JERRY
print(arr[3])		ROSE

--从上面的结果可以看出来,数组的下标默认是从1开始的。所以上述创建数组,也可以通过如下方式来创建
>arr = {}
>arr[1] = "TOM"
>arr[2] = "JERRY"
>arr[3] = "ROSE"

--上面我们说过了,表的索引即可以是数字,也可以是字符串等其他的内容,所以我们也可以将索引更改为字符串来创建
>arr = {}
>arr["X"] = 10
>arr["Y"] = 20
>arr["Z"] = 30

--当然,如果想要获取这些数组中的值,可以使用下面的方式
--方式一
>print(arr["X"])
>print(arr["Y"])
>print(arr["Z"])
--方式二
>print(arr.X)
>print(arr.Y)
>print(arr.Z)

--当前table的灵活不进于此,还有更灵活的声明方式
>arr = {"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30}

--如何获取上面的值?
TOM :  arr[1]
10  :  arr["X"] | arr.X
JERRY: arr[2]
20  :  arr["Y"] | arr.Y
ROESE?
(6)function
  • 在 Lua语言中,函数( Function )是对语句和表达式进行抽象的主要方式。

  • 定义函数的语法为:

function functionName(params)

end
  • 函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua 语言会通过 抛弃多余参数和将不足的参数设为 nil 的方式来调整参数的个数。
function  f(a,b)
print(a,b)
end

f()		--> nil  nil
f(2)	--> 2 nil
f(2,6)	--> 2 6
f(2.6.8)	--> 2 6 (8被丢弃)
  • 可变长参数函数
function add(...)
a,b,c=...
print(a)
print(b)
print(c)
end

add(1,2,3)  --> 1 2 3
  • 函数返回值可以有多个,这点和Java不太一样
function f(a,b)
return a,b
end

x,y=f(11,22)	--> x=11,y=22	
(7)thread
  • thread翻译过来是线程的意思,在Lua中,thread用来表示执行的独立线路,用来执行协同程序。
-- 定义一个简单的协程函数
function simpleCoroutine()
    print("Coroutine started")
    coroutine.yield("Hello from coroutine!")
    print("Coroutine resumed")
end

-- 创建协程
local co = coroutine.create(simpleCoroutine)

-- 启动协程
local success, result = coroutine.resume(co)

-- 协程会打印 "Coroutine started" 然后暂停并返回结果
print("Main thread received: " .. result)  -- 打印从协程返回的消息

-- 再次启动协程,协程将继续执行直到结束
success, result = coroutine.resume(co)

-- 打印 "Coroutine resumed"
-- 由于协程已经结束,这里不会打印更多的信息
(8)userdata
  • userdata是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型。

4、Lua控制结构

  • Lua 语言提供了一组精简且常用的控制结构,包括用于条件执行的then以及用于循环的 while、 repeat 和 for。
  • 所有的控制结构语法上都有一个显式的终结符: end 用于终结 if、 for 及 while 结构, until 用于终结 repeat 结构。
(1)if then else
  • if语句先测试其条件,并根据条件是否满足执行相应的 then 部分或 else 部分。 else 部分是可选的。
function testif(a)
 if a>0 then
 	print("a是正数")
 end
end

function testif(a)
 if a>0 then
 	print("a是正数")
 else
 	print("a是负数")
 end
end
  • 如果要编写嵌套的 if 语句,可以使用 elseif。 它类似于在 else 后面紧跟一个if。根据传入的年龄返回不同的结果,如
age<=18 青少年,
age>18 , age <=45 青年
age>45 , age<=60 中年人
age>60 老年人

function show(age)
if age<=18 then
 return "青少年"
elseif age>18 and age<=45 then
 return "青年"
elseif age>45 and age<=60 then
 return "中年人"
elseif age>60 then
 return "老年人"
end
end
(2)while循环
  • 顾名思义,当条件为真时 while 循环会重复执行其循环体。 Lua 语言先测试 while 语句 的条件,若条件为假则循环结束;否则, Lua 会执行循环体并不断地重复这个过程。

  • 语法:

while 条件 do
  循环体
end
  • 案例:实现数组的循环
function testWhile()
 local i = 1
 while i<=10 do
  print(i)
  i=i+1
 end
end
(3)repeat循环
  • 顾名思义, repeat-until语句会重复执行其循环体直到条件为真时结束。 由于条件测试在循环体之后执行,所以循环体至少会执行一次。

  • 语法:

repeat
 循环体
until 条件
  • 案例:
function testRepeat()
 local i = 10
 repeat
  print(i)
  i=i-1
 until i < 1
end
(4)for循环
  • 数值型for循环

  • 语法:

for param=exp1,exp2,exp3 do
 循环体
end
  • 案例:param的值从exp1变化到exp2之前的每次循环会执行循环体,并在每次循环结束后将步长(step)exp3增加到param上。exp3可选,如果不设置默认为1
for i = 1,100,10 do
print(i)
end

--输出结果
1
11
21
31
41
51
61
71
81
91
  • 泛型for循环:泛型for循环通过一个迭代器函数来遍历所有值,类似于java中的foreach语句。

  • 语法:

for i,v in ipairs(x) do
	循环体
end
  • 案例:i是数组索引值,v是对应索引的数组元素值,ipairs是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。
arr = {"TOME","JERRY","ROWS","LUCY"}
for i,v in ipairs(arr) do
 print(i,v)
end

--输出结果
1	TOM
2	JERRY
3	ROWS
4	LUCY
  • 但是如果将arr的值进行修改,同样的代码在执行的时候,就只能看到和之前一样的结果,而其中的x为JACK就无法遍历出来
arr = {"TOME","JERRY","ROWS",x="JACK","LUCY"}
for i,v in ipairs(arr) do
 print(i,v)
end

--输出结果
1	TOM
2	JERRY
3	ROWS
4	LUCY
  • 我们可以将迭代器函数变成pairs,就能遍历出来了
for i,v in pairs(arr) do
 print(i,v)
end

--输出结果
1	TOM
2	JERRY
3	ROWS
4	LUCY
x	JACK
10-26 11:30