Lapp Framework |
|
原始來源在
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
。如果選項有長名稱,則該長名稱會優先用做欄位名稱。簡單標旗不需要類型或預設值指定符,因為預設類型為 布林值
。
參數 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
帶有尾隨的「...」,表示此參數為「可變長度引數」參數。它必須是最後一個參數,且 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
範例,以取得另一種實作方式。)
標記和選項也可以宣告為可變長度引數陣列,且可以在任何地方出現。請記住簡短選項可以合併(如「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