Lapp 框架 |
|
原始代码位于 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 巫术,所以考虑以下情况
not args.v[1]
为 true
,因此vlevel
变为 0。
如果脚本实现了 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 中有一个小建议 - 在 lapp.lua 的第 178 行之前添加类似 'local typespec' 的内容。这种更改可能会让 'strict' 更开心。
Lapp 在与 Lua 相同的许可下提供。
版权所有,SteveDonovan,2009