Lua源码笔记–命令行参数

Lua的命令行参数放在一个arg的table里面。arg[0]存放脚本文件,arg[1…n]存放参数。

@(语法)

lua [options] [script [args]]

注意:从源码上看最大参数个数不能超过8000,由宏LUAI_MAXCSTACK定义。

#define LUAI_MAXCSTACK 8000

一个简单的例子

@(arg.lua)

for i, v in pairs(arg) do
	print(i, v)
end

运行结果:

[root@VM_89_131_centos lua]# lua arg.lua 1 2 3
1	1
2	2
3	3
-1	lua
0	arg.lua

由上面运行情况可知:
arg[-1]: lua执行文件
arg[0]: 执行脚本
arg[1]: 第一个参数
arg[2]: 第二个参数
arg[n]: 第n个参数

源码分析

下面的源码分析将用到上面的例子做讲解,命令行的源码主要集中在两个函数:
@(lua.c)

static int getargs (lua_State *L, char **argv, int n) {
	int narg;
	int i;
	int argc = 0;
	while (argv[argc]) argc++;  
	narg = argc - (n + 1); 
	luaL_checkstack(L, narg + 3, "too many arguments to script");
	for (i=n+1; i < argc; i++)
	  	lua_pushstring(L, argv[i]);
	lua_createtable(L, narg, n + 1);
	for (i=0; i < argc; i++) {
	  	lua_pushstring(L, argv[i]);
	  	lua_rawseti(L, -2, i - n);
	}
	return narg;
}

这个函数主要做如下:

  • 第5行: 获取所有参数个数。
  • 第6行: 计算出narg脚本参数个数。
  • 第7行: 检查参数个数是否超过最大,如有则返回错误。
  • 第8,9行: 将参数值PUSH到堆栈。
  • 第10行: 创建一个table,并压入堆栈。这个table就是我们在脚本用到的arg,只是现在还没有命名。
  • 第11-14行: 把argv[0-4]的值插入到新创建table,且插入索引为-1,0,1,2,3,这也印证了上面示例运行的情况。
static int handle_script (lua_State *L, char **argv, int n) {
  ...
  int narg = getargs(L, argv, n); 
  lua_setglobal(L, "arg");
  ...
}

这个函数3-4行的作用通俗讲就是把新建的table命名为arg,实现的方式就是把这个新table放入全局的_G表。这样就可以直接在lua脚本中使用arg数组了。
@(伪代码:)

_G["arg"] = newTable

进阶

@(arg1.lua)

print(...)

运行结果:

[root@VM_89_131_centos lua]# lua arg1.lua 1 2 3
1	2	3

可以看到直接输出了脚本运行参数。

原因:
一个Lua脚本在实现的时候是当做一个函数的,在脚本内定义的函数可以理解成内嵌函数。可以想象成每个脚本都有个main函数,main函数接受可变成参数。

function main(...) 
	-- script content
	print(...)
end

getargs函数的8-9行所做的工作就是为这个函数设置参数。

困惑

在getargs函数中的参数 int n 是什么意思?从代码上看,这个参数n在整个参数解析的时候还挺重要的。

static int getargs (lua_State *L, char **argv, int n) {
	narg = argc - (n + 1);
}

参数n: 表示脚本文件在整个命令行参数的位置(从0开始),如

  • /usr/bin/lua test.lua 1 2 3 n=1
  • /usr/bin/lua -e “print(“start”)” test.lua 1 2 3 n=3

总结:
命令行参数的代码很短,但想看懂确不容易,主要是要弄懂Lua堆栈的实现,这个会在后面详细介绍。