Lapp Framework

lua-users home
Wiki

讓製作簡單命令列腳本變容易

原始來源在

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 的巫毒,因此請考量下列情況

定義參數回呼函式

如果腳本實作 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

另請參閱


RecentChanges · 喜好設定
編輯 · 歷史
最後編輯於西元 2015 年 5 月 16 日下午 9:12 GMT (差異)