Lapp Framework |
|
原始来源在 http://mysite.mweb.co.za/residents/sdonovan/lapp.zip;现已加入 PenlightLibraries,请参见 [1]。
Lapp 是一个小型、专注的 Lua 模块,旨在让标准的命令行解析变得更轻松、更直观。它实现了标准的 GNU 风格,即单字母的短选项以 '-' 开头,可能还有一个以 '--' 开头的长选项。通常,需要参数的选项期望在下一个参数中找到它们(例如,“gcc test.c -o test”),但接受数字参数的单个短选项可以省略空格(例如,“head -n4 test.c”)。
Lapp 会尽可能将参数转换为等效的 Lua 类型,即转换数字并将其转换为文件对象。如果任何转换失败,或者缺少必需的参数,将发出错误并显示用法文本。因此,有两项必要任务:提供标志和选项名称,并将它们与类型关联。
对于任何非简单的脚本,即使是仅供个人使用的脚本,提供用法文本也是必要的。Lapp 的新颖之处在于它从这个角度出发,定义了一种松散的用法字符串格式,该格式可以指定参数的名称和类型。
一个例子将使这一点更清楚
-- scale.lua require 'lapp' local args = lapp [[ Does some calculations -o,--offset (default 0.0) Offset to add to scaled number -s,--scale (number) Scaling factor <number> (number) Number to be scaled ]] print(args.offset + args.scale * args.number)
这是使用此脚本的命令行会话
D:\dev\lua\lapp>lua scale.lua
scale.lua:missing required parameter: scale
Does some calculations
-o,--offset (default 0.0) Offset to add to scaled number
-s,--scale (number) Scaling factor
<number> (number ) Number to be scaled
D:\dev\lua\lapp>lua scale.lua -s 2.2 10
22
D:\dev\lua\lapp>lua scale.lua -s 2.2 x10
scale.lua:unable to convert to number: x10
....(usage as before)
Lapp 用法字符串中有两种有意义的行:选项行和参数行。选项行给出短选项,后面可以选择性地跟着对应的长选项。括号中的类型说明符可以跟随。类似地,参数行以 '<' PARAMETER '>' 开头,后跟类型说明符。类型说明符的形式为 '(default ' VALUE ')' 或 '(' TYPE ')';默认说明符意味着参数或选项具有默认值,并且 *不是必需* 的。TYPE 是 'string'、'number'、'file-in' 或 'file-out' 之一;VALUE 是一个数字、('stdin','stdout','stderr') 中的一个或一个令牌。行中的其余部分不会被解析,可用于解释性文本。
此脚本显示了指定的参数名称与输出表中的字段之间的关系。
-- simple.lua local args = require ('lapp') [[ Various flags and option types -p A simple optional flag, defaults to false -q,--quiet A simple flag with long name -o (string) A required option with argument <input> (default stdin) Optional input file parameter ]] for k,v in pairs(args) do print(k,v) end
我刚刚转储了 `args` 表的所有值;请注意,`args.quiet` 已变为 `true`,因为它已指定;`args.p` 默认为 `false`。如果选项有长名称,将优先使用该名称作为字段名。对于简单的标志,不需要类型或默认值说明符,因为默认类型是 `boolean`。
参数 `input` 已设置为打开的只读文件对象 - 我们知道它必须是只读文件,因为这是默认值的类型。`input_name` 字段是自动生成的,因为它通常可以方便地访问原始文件名。
D:\dev\lua\lapp>simple -o test -q simple.lua p false input file (781C1BD8) quiet true o test input_name simple.lua D:\dev\lua\lapp>simple -o test simple.lua one two three 1 one 2 two 3 three p false quiet false input file (781C1BD8) o test input_name simple.lua
请注意,提供的任何额外参数都将放入结果表中,并带有整数索引,即 `args[i]`,其中 `i` 从 1 到 `#args`。
对于任务明确、完成迅速的简单脚本,文件不必显式关闭,因为文件对象被垃圾回收的结果是关闭它们。
类型说明符也可以是 '(' MIN '..' MAX ')' 的形式。
require 'lapp' local args = lapp [[ Setting ranges <x> (1..10) A number from 1 to 10 <y> (-5..1e6) Bigger range ]] print(args.x,args.y)
这里的含义是值大于或等于 MIN 且小于或等于 MAX;没有提供强制参数为整数的机制。
您还可以定义可用于类型说明符的自定义类型
require ('lapp') lapp.add_type('integer','number', function(x) lapp.assert(math.ceil(x) == x, 'not an integer!') end ) local args = lapp [[ <ival> (integer) Process PID ]] print(args.ival)
`lapp.add_type` 接受三个参数:类型名称、转换器和约束函数。约束函数预计会抛出断言,如果某个条件不为真;我们使用 `lapp.assert`,因为它以命令行脚本的标准方式失败。转换器参数可以是 Lapp 已知的类型名称,也可以是一个接受字符串并生成值的函数。
require 'lapp' local args = lapp [[ Summing numbers <numbers...> (number) A list of numbers to be summed ]] local sum = 0 for i,x in ipairs(args.numbers) do sum = sum + x end print ('sum is '..sum)
参数 `number` 带有尾随的 '...',这表明该参数是“varargs”参数。它 *必须* 是最后一个参数,而 `args.number` 将是一个数组。
考虑一下这个类 Unix 系统 `head` 工具的实现
-- implements a BSD-style head -- (see http://www.manpagez.com/man/1/head/osx-10.3.php) require ('lapp') local args = lapp [[ Print the first few lines of specified files -n (default 10) Number of lines to print <files...> (default stdin) Files to print ]] -- by default, lapp converts file arguments to an actual Lua file object. -- But the actual filename is always available as <file>_name. -- In this case, 'files' is a varargs array, so that 'files_name' is -- also an array. local nline = args.n local nfile = #args.files for i = 1,nfile do local file = args.files[i] if nfile > 1 then print('==> '..args.files_name[i]..' <==') end local n = 0 for line in file:lines() do print(line) n = n + 1 if n == nline then break end end end
请注意,我们如何能够访问所有文件名,因为自动生成的字段 `files_name` 也是一个数组!
(这可能不是一个非常体贴的脚本,因为 Lapp 将打开提供的所有文件,并在脚本结束时才关闭它们。请参阅 `xhead.lua` 示例以获取另一个实现。)
标志和选项也可以声明为 vararg 数组,并且可以出现在任何位置。请记住,短选项可以组合(如“tar -xzf”),因此允许出现“-vvv”。但通常 `args.v` 的值只是一个简单的 `true`。
local args = require ('lapp') [[ -v... Verbosity level; can be -v, -vv or -vvv ]] vlevel = not args.v[1] and 0 or #args.v print(vlevel)
`vlevel` 的赋值是 Lua 的一点魔术,所以考虑以下情况
如果脚本实现了 `lapp.callback`,那么 Lapp 将在解析完每个参数后调用它。回调函数将接收参数名称、原始未解析值和结果表。它在赋值后立即调用,因此相应的字段是可用的。
require ('lapp') function lapp.callback(parm,arg,args) print('+',parm,arg) end local args = lapp [[ Testing parameter handling -p Plain flag (defaults to false) -q,--quiet Plain flag with GNU-style optional long name -o (string) Required string option -n (number) Required number option -s (default 1.0) Option that takes a number, but will default <start> (number) Required number argument <input> (default stdin) A parameter which is an input file <output> (default stdout) One that is an output file ]] print 'args' for k,v in pairs(args) do print(k,v) end
这会产生以下输出
D:\dev\lua\lapp>args -o name -n 2 10 args.lua
+ o name
+ n 2
+ start 10
+ input args.lua
args
p false
s 1
input_name args.lua
quiet false
output file (781C1B98)
start 10
input file (781C1BD8)
o name
n 2
为什么回调有用?在某些情况下,您希望在解析参数后立即采取行动。
非常有意思的想法!也许这将导致其他编程语言的克隆 :) 无论如何,感谢您的这个方法和编写的代码。
lapp.lua 中的一个小建议 - 类似 'local typespec' 这样的东西,在 lapp.lua 的 178 行之前。这类更改可能会让 'strict' 模式更愉快。
Lapp 可在 Lua 的相同许可证下使用。
版权所有,SteveDonovan, 2009