Lua 入门快速教程:从零搭建环境到写出第一个脚本

一篇快速上手的 Lua 入门教程:涵盖 Windows/macOS/Linux 环境搭建、基本语法、数据类型、控制结构、函数、表(table)、模块与错误处理,附完整可运行示例,适合零基础读者在 30 分钟内写出第一个 Lua 脚本。

Lua 是一种轻量级、可嵌入的脚本语言,解释器核心仅约 2 万行 C 代码,设计目标是"用最小的体积提供最大的扩展能力"。它语法简洁、易于学习,被广泛用于游戏开发(魔兽世界、Roblox)、Web 网关(OpenResty)、嵌入式系统等场景。本篇教程带你在 30 分钟内完成环境搭建并写出第一个 Lua 脚本。

1. 环境搭建

macOS(推荐 Homebrew):

brew install lua

Linux(Debian/Ubuntu):

sudo apt-get install lua5.4

Windows:从 LuaBinaries 下载预编译二进制,解压后将 lua54.exe 所在目录加入 PATH 即可。

安装完成后验证:

lua -v
# Lua 5.4.x  Copyright (C) 1994-2024 Lua.org, PUC-Rio

用 REPL 交互式学习

直接输入 lua 即可进入交互式解释器(REPL),这是学习语法最快的方式——每写一行立刻看到结果,不用保存文件再运行:

$ lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> print("hello")
hello
> = 1 + 2        -- REPL 中可以用 = 快速打印表达式
3
> name = "lua"   -- 定义变量
> = name:upper() -- 立刻验证结果
LUA

几个 REPL 小技巧:

  • = 表达式 代替 print(表达式),更省事;
  • 多行代码(如函数定义)直接逐行输入,REPL 会等待 end 再执行;
  • 输入 os.exit() 或按 Ctrl+D 退出。

编辑器配置:VSCode + Lua 插件

学习阶段推荐 VSCode,安装 sumneko.lua(Lua Language Server)插件,可以获得:

  • 语法高亮与自动补全;
  • 实时的类型检查与未定义变量警告(能帮你提前发现把局部变量写成全局的笔误);
  • 悬停查看标准库函数的文档。

配合 Code Runner 类插件,按一个快捷键就能运行当前 .lua 文件,学习体验会顺畅很多。

关于 Lua 版本:5.1 / 5.3 / 5.4 学哪个?

Lua 的版本差异比 Python 2/3 温和得多,但仍有几个关键点需要注意:

版本现状典型用户
Lua 5.1老但应用极广LuaJIT、OpenResty、魔兽世界插件
Lua 5.3引入整数/浮点区分、位运算部分嵌入式项目
Lua 5.4当前最新稳定版官方推荐的新项目

建议:自学直接用 Lua 5.4;如果目标明确是 OpenResty 或游戏热更新(基于 LuaJIT),再补 5.1 的差异即可。本教程代码同时兼容 5.3 和 5.4。各版本的详细差异与选型分析见 Lua 版本对比

2. 运行第一个脚本

新建文件 hello.lua

print("Hello, Lua!")

运行:

lua hello.lua

3. 基本语法

  • 注释:单行用 --,多行用 --[[ ... ]]
  • 语句:不需要分号结尾(写了也合法)
  • 变量:默认是全局变量,用 local 声明局部变量(推荐,避免污染全局环境)
-- 单行注释
--[[
  多行注释
]]

local name = "Lua"      -- 局部变量
version = 5.4           -- 全局变量(不推荐)

新手常见坑:Lua 变量不加 local 就是全局的。函数内部忘了写 local,变量会泄漏到全局环境,和其他模块的同名变量互相覆盖,是非常隐蔽的 bug 来源。养成"默认都写 local“的习惯。

4. 数据类型

Lua 只有 8 种基本类型,常用的有:

类型示例说明
nilnil表示"无值”,未定义的变量即为 nil
booleantrue / false条件判断中,只有 nil 和 false 为假
number423.14Lua 5.3+ 区分整数与浮点
string"hello"单双引号均可,.. 进行拼接
table{1, 2, 3}唯一的复合数据结构
functionfunction() end函数是一等公民
local a = nil
local b = true
local c = 42
local d = "hello"
print(type(d))  -- string

新手常见坑:在 Lua 的条件判断里,只有 nilfalse 为假0 和空字符串 "" 都是真!这和 Python、JavaScript 都不一样:

if 0 then print("0 是真值") end    -- 会打印!
if "" then print("空串也是真") end -- 会打印!

5. 控制结构

条件判断

local score = 85

if score >= 90 then
  print("优秀")
elseif score >= 60 then
  print("及格")
else
  print("不及格")
end

循环

-- while
local i = 1
while i <= 3 do
  print(i)
  i = i + 1   -- 注意:Lua 没有 ++ 运算符
end

-- 数值 for
for i = 1, 5 do
  print(i)        -- 1 2 3 4 5
end

for i = 10, 1, -2 do
  print(i)        -- 10 8 6 4 2(第三个参数是步长)
end

-- repeat-until(至少执行一次)
local n = 0
repeat
  n = n + 1
until n >= 3

新手常见坑:Lua 的 for i = 1, 5闭区间,会执行到 5,这点和 C/Java 的 for (i=1; i<5; i++) 直觉相反。另外 Lua 没有 ++--+= 这些运算符,只能写 i = i + 1

6. 函数

函数用 function 定义,支持多返回值:

-- 定义与调用
local function greet(name)
  return "Hello, " .. name .. "!"
end

print(greet("World"))  -- Hello, World!

-- 多返回值
local function divmod(a, b)
  return a // b, a % b
end

local q, r = divmod(10, 3)
print(q, r)  -- 3  1

新手常见坑:注意 /// 的区别——/ 是浮点除法,// 是整数除法(向下取整):

print(7 / 2)   -- 3.5
print(7 // 2)  -- 3
print(10 / 2)  -- 5.0(注意是浮点数 5.0)

从 Python 2 转来的读者尤其要注意:Lua 的 / 永远是浮点除法。

7. 表(table):Lua 的核心

table 是 Lua 中唯一的复合数据结构,可以同时当数组和字典用:

-- 作为数组(注意:下标从 1 开始!)
local fruits = {"apple", "banana", "cherry"}
print(fruits[1])  -- apple
print(#fruits)    -- 3(# 取长度)

-- 作为字典
local person = {name = "John", age = 30}
print(person.name)   -- John
print(person["age"]) -- 30

-- 遍历
for i, fruit in ipairs(fruits) do
  print(i, fruit)
end

for key, value in pairs(person) do
  print(key, value)
end

新手常见坑 1table 下标从 1 开始,不是 0! fruits[0]nil,这是所有从 C/Java/Python 转来的人必踩的第一个坑。

新手常见坑 2pairs 遍历的顺序不保证是插入顺序;要按顺序遍历数组部分请用 ipairs。另外 ipairs 遇到 nil 空洞就会停止,所以数组中间不能有 nil

新手常见坑 3:字符串比较直接用 == 即可,Lua 的字符串是不可变且被内部驻留(interned)的,== 比较的就是内容,不需要像 Java 那样用 equals

local a = "hello"
local b = "he" .. "llo"
print(a == b)  -- true

想深入 table 与泛型 for 的细节(比如自定义迭代器),可以继续读 Lua 数组与迭代器

8. 字符串操作

local s = "hello"
print(s .. " world")      -- 拼接:hello world
print(string.upper(s))    -- HELLO
print(string.len(s))      -- 5
print(string.format("%d-%s", 42, "lua"))  -- 42-lua

Lua 还有一套自己的模式匹配语法(类似但不同于正则表达式),用于查找、替换、提取字符串,是文本处理的利器。进阶用法见 Lua 字符串模式匹配

9. 模块与错误处理

require 加载模块,用 pcall 捕获错误:

-- mymodule.lua
local M = {}
function M.say_hi()
  return "hi"
end
return M
-- main.lua
local m = require("mymodule")
print(m.say_hi())

-- 错误处理
local ok, err = pcall(function()
  error("出错了")
end)
print(ok, err)  -- false  出错了

模块系统(require 的搜索路径、package.path、LuaRocks 包管理)和错误处理(pcall/xpcall 的区别)都有更多细节,分别参考 Lua 模块与包管理Lua 错误处理最佳实践

10. Lua 与其他语言的直观对比

如果你已经会 Python 或 JavaScript,下面这张速查表能帮你快速建立直觉:

主题LuaPython / JavaScript
数组下标1 开始从 0 开始
假值只有 nilfalse0""null 等都是假
代码块then / do / end 包裹缩进(Python)或 {}(JS)
字符串拼接..+
不等于~=!=
局部变量local xlet x / 直接赋值
哈希表table(数组字典二合一)dict / Object
多返回值原生支持 return a, bPython 靠元组,JS 靠数组解构

几个值得单独记住的点:

  1. 一切皆 table:Python 的 list、dict,JS 的数组、对象,在 Lua 里统一用 table 表达,甚至连面向对象也是用 table + 元表模拟出来的(见 Lua 面向对象编程)。
  2. 函数是一等公民:和 JavaScript 一样,函数可以赋值给变量、作为参数传递。Lua 的闭包机制非常干净,详见 Lua 闭包与 Upvalue
  3. 没有类和异常:Lua 没有 class 关键字,也没有 try/except,错误处理用 pcall 把函数包起来"试调用"。
  4. 协程是语言内置的:不需要 async/await 语法,协程是标准库的一部分,进阶内容见 Lua 协程深入解析

如果你正在权衡要不要学 Lua,也可以看看 Lua 与 Python 的对比,了解两者各自适合的场景。

11. 综合示例:命令行记账本

把上面的知识组合起来,写一个比"成绩统计"更有意思的小项目——一个简易记账本,支持记一笔、看账单、算结余:

-- ledger.lua:简易命令行记账本
local ledger = {
  records = {},  -- 每条记录:{item=项目, amount=金额(正为收入,负为支出)}
}

-- 记一笔账
function ledger.add(item, amount)
  table.insert(ledger.records, {item = item, amount = amount})
end

-- 计算总结余
function ledger.balance()
  local sum = 0
  for _, r in ipairs(ledger.records) do
    sum = sum + r.amount
  end
  return sum
end

-- 打印账单
function ledger.report()
  print(string.format("%-4s %-12s %10s", "编号", "项目", "金额"))
  print(string.rep("-", 30))
  for i, r in ipairs(ledger.records) do
    print(string.format("%-4d %-12s %+10.2f", i, r.item, r.amount))
  end
  print(string.rep("-", 30))
  print(string.format("总结余: %.2f 元", ledger.balance()))
end

-- 录入几笔账
ledger.add("工资", 8000)
ledger.add("房租", -2500)
ledger.add("午餐", -35)
ledger.add("卖旧书", 120)

ledger.report()

运行结果:

编号  项目               金额
------------------------------
1    工资           +8000.00
2    房租           -2500.00
3    午餐             -35.00
4    卖旧书          +120.00
------------------------------
总结余: 5585.00 元

这个例子用到了本教程的大部分知识点:

  • table 嵌套records 是一个数组,每个元素又是一个字典;
  • 函数作为 table 的字段ledger.add 这种写法是 Lua 组织代码的常见风格,再进一步就演变成面向对象;
  • 泛型 for + ipairs:按顺序遍历数组;
  • string.format%-12s 左对齐、%+10.2f 强制带符号并保留两位小数;
  • string.rep:重复字符串画出分隔线。

可以试着给它加功能:比如按类别统计支出、把账单保存到文件(用 io.open),作为练习。

下一步学什么

完成本教程后,你已经掌握了 Lua 的基础语法。建议按下面的顺序继续深入:

  1. Lua 的数组与迭代器 —— 深入理解 table 与泛型 for
  2. Lua 元表与元方法 —— Lua 最强大的机制
  3. Lua 模块与包管理 —— 组织大型项目
  4. Lua 错误处理最佳实践 —— 写出健壮的代码

完整的学习路径可以参考 Lua 学习路线图,或回到 Lua 专题导航 查看全部文章。

常见问题(FAQ)

Lua 适合零基础作为第一门编程语言吗?

适合。Lua 的语法规则极少(关键字只有 20 来个),没有类和异常等复杂概念,8 种数据类型一天就能过完。很多游戏引擎(如 Roblox)正是用它教青少年编程。不过要注意 Lua 的入门教程和社区规模比 Python 小,遇到冷门问题时查资料会稍费劲。

学 Lua 还是学 Python?

取决于目标:想做游戏脚本、嵌入式、OpenResty 网关,选 Lua;想做数据分析、AI、自动化办公,选 Python。Lua 的优势是解释器极小、嵌入 C/C++ 项目非常容易;Python 的优势是生态庞大。详细的取舍分析见 Lua 与 Python 的对比

Lua 5.1 和 5.4 学哪个版本?

自学直接用 5.4(当前最新稳定版),本教程代码均兼容 5.3/5.4。只有当你的目标平台锁定旧版本时才需要补差异:比如 OpenResty 和 LuaJIT 基于 5.1,主要区别是 5.1 没有位运算、没有整数除法 //,且 setfenv 等旧机制已被 _ENV 取代。

为什么我的 Lua 数组第一个元素取不到?

大概率是你用了下标 0。Lua 的数组约定下标从 1 开始,arr[0]nil(不会报错,更容易迷惑)。遍历时用 ipairs 会自动从 1 开始,能避开这个问题。

学完这篇教程后,多久能上手写游戏脚本?

如果每天投入 1-2 小时,掌握本教程内容后再花一到两周学习 元表面向对象,就可以开始读游戏项目里的 Lua 脚本了。建议按 Lua 学习路线图 的八周计划推进。

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「lua」更多文章