CommonLisp中解析命令行参数示例

2022-08-22 15:01:05
目录
clingon子命令选项与参数选项名称必要性与默认值可多次使用的选项信号选项选择型选项

clingon

clingon>

#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
  (ros:ensure-asdf)
  #+quicklisp(ql:quickload '(clingon) :silent t)
  )
(defpackage :ros.script.hello.3868869124
  (:use :cl
        :clingon))
(in-package :ros.script.hello.3868869124)
(defun top-level/handler (cmd)
  (check-type cmd clingon:command)
  (let ((count (clingon:getopt cmd :count))
        (name (first (clingon:command-arguments cmd))))
    (dotimes (_ count)
      (declare (ignorable _))
      (format t "Hello ~A!~%" name))))
(defun main (&rest argv)
  (let ((app (clingon:make-command
              :handler #'top-level/handler
              :name "hello"
              :options (list
                        (clingon:make-option
                         :integer
                         :description "number of greetings"
                         :initial-value 1
                         :key :count
                         :long-name "count")))))
    (clingon:run app argv)))
;;; vim: set ft=lisp lisp:

稍微做一些解释。首先执行命令ros init hello生成上面的代码的雏形——加载依赖、包定义,以及空的函数main。为了加载 clingon,将其作为函数ql:quickload的参数。然后分别定义一个commandhandler,以及option

在 clingon 中,类clingon:command的实例对象表示一个可以在 shell 中被触发的命令,它们由函数clingon:make-command创建。每一个命令起码要有三个要素:

    :handler,负责使用命令行选项、实现业务逻辑的函数;:name,命令的名字,一般会被展示在命令的用法说明中;:options,该命令所接受的选项。

    此处的:handler就是函数top-level/handler,它会被函数clingon:run调用(依赖注入的味道),并将一个合适的clingon:command对象传入。:options目前只承载了一个选项的定义,即

                            (clingon:make-option
                             :integer
                             :description "number of greetings"
                             :initial-value 1
                             :key :count
                             :long-name "count")
    

    它定义了一个值为整数的选项,在命令行中通过--count指定。如果没有传入该选项,那么在使用函数clingon:getopt取值时,会获得默认值 1。如果要从一个命令对象中取出这个选项的值,需要以它的:key参数的值作为参数来调用函数clingon:getopt,正如上面的函数top-level/handler所示。

    子命令

    clingon>git add、git branch这样的子命令特性。像addbranch这样的子命令,对于 clingon 而言仍然是类clingon:command的实例对象,只不过它们不会传递给函数clingon:run调度,而是传递给函数clingon:make-command的参数:sub-command,如下列代码所示

    (defun top-level/handler (cmd)
      (declare (ignorable cmd)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "cli"
                  :sub-commands (list
                                 (clingon:make-command
                                  :handler #'(lambda (cmd)
                                               (declare (ignorable cmd))
                                               (format t "Dropped the database~%"))
                                  :name "dropdb")
                                 (clingon:make-command
                                  :handler #'(lambda (cmd)
                                               (declare (ignorable cmd))
                                               (format t "Initialized the database~%"))
                                  :name "initdb")))))
        (clingon:run app argv)))
    

    选项与参数

    在>

    例如在第一个例子中,就定义了一个名为--count的选项,它在解析结果中被赋予了:count这个关键字,可以通过函数clingon:getopt来引用它的值;

    与之相反,变量name是从命令行中解析了选项后、剩余的参数中的第一个,它是以位置来标识的。clingon 通过函数clingon:make-option来定义选项,它提供了丰富的控制能力。

    选项名称

    选项有好几种名字,一种叫做:key,是在程序内部使用的名字,用作函数clingon:getopt的参数之一;

    一种叫做:long-name,一般为多于一个字符的字符串,如"count",在命令行该名称需要带上两个连字符的前缀来使用,如--count>;

    最后一种叫做:short-name,为一个单独的字符,如#\v,在命令行中带上一个连字符前缀来使用,如-v

    必要性与默认值

    通过传入参数:required>给函数clingon:make-option,可以要求一个选项为必传的。

    例如下面的命令的选项--n就是必传的

    (defun top-level/handler (cmd)
      (dotimes (i (clingon:getopt cmd :n))
        (declare (ignorable i))
        (format t ".")))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "dots"
                  :options (list
                            (clingon:make-option
                             :integer
                             :description "打印的英文句号的数量"
                             :key :n
                             :long-name "n"
                             :required t)))))
        (clingon:run app argv)))
    

    如果不希望在一些最简单的情况下也要繁琐地编写--n 1这样的命令行参数,可以用:initial-value 1来指定。除此之外,也可以让选项默认读取指定的环境变量中的值,使用:env-vars指定环境变量名即可

    (defun top-level/handler (cmd)
      (format t "Hello ~A~%" (clingon:getopt cmd :username)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "greet"
                  :options (list
                            (clingon:make-option
                             :string
                             :description "用户名"
                             :env-vars '("GREETER_USERNAME")
                             :key :username
                             :long-name "username")))))
        (clingon:run app argv)))
    

    可多次使用的选项

    curl中的选项-H就是可以多次使用的,每指定一次就可以在请求中添加一个>

    在 clingon 中可以通过往函数clingon:make-option传入:list来实现。当用clingon:getopt取出类型为:list的选项的值时,得到的是一个列表,其中依次存放着输入的值的字符串。

    (defun top-level/handler (cmd)
      (let ((messages (clingon:getopt cmd :message)))
        (format t "~{~A~^~%~}" messages)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "commit"
                  :options (list
                            (clingon:make-option
                             :list
                             :description "提交的消息"
                             :key :message
                             :long-name "message"
                             :short-name #\m)))))
        (clingon:run app argv)))
    

    另一种情况是尽管没有值,但仍然多次使用同一个选项。例如命令ssh的选项-v,使用的次数越多(最多为 3 次),则ssh打印的调试信息也就越详细。这种类型的选项在 clingon 中称为:counter

    (defun top-level/handler (cmd)
      (format t "Verbosity: ~D~%" (clingon:getopt cmd :verbose)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "log"
                  :options (list
                            (clingon:make-option
                             :counter
                             :description "啰嗦程度"
                             :key :verbose
                             :long-name "verbose"
                             :short-name #\v)))))
        (clingon:run app argv)))
    

    信号选项

    有一些选项只需要区分【有】和【没有】两种情况就可以了,而不需要在意这个选项的值——或者这类选项本身就不允许有值,例如docker>命令的选项-d--detach

    这种选项的类型为:boolean/true,如果指定了这个选项,那么取出来的值始终为t。与之相反,类型:boolean/false取出来的值始终为nil

    (defun top-level/handler (cmd)
      (let ((rv (software-type)))
        (when (clingon:getopt cmd :shout)
          (setf rv (concatenate 'string (string-upcase rv) "!!!!111")))
        (format t "~A~%" rv)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "info"
                  :options (list
                            (clingon:make-option
                             :boolean/true
                             :description "大喊"
                             :key :shout
                             :long-name "shout")))))
        (clingon:run app argv)))
    

    选择型选项

    如果一个选项尽管接受的是字符串,但并非所有输入都是有意义的,例如命令dot的选项-T。从dot的>pspdfpng等。

    比起声明一个:string类型的选项,让 clingon 代劳输入值的有效性检查来得更轻松,这里可以使用:choice类型

    (defun top-level/handler (cmd)
      (format t "~A~%" (clingon:getopt cmd :hash-type)))
    (defun main (&rest argv)
      (let ((app (clingon:make-command
                  :handler #'top-level/handler
                  :name "digest"
                  :options (list
                            (clingon:make-option
                             :choice
                             :description "哈希类型"
                             :items '("MD5" "SHA1")
                             :key :hash-type
                             :long-name "hash-type")))))
        (clingon:run app argv)))

    以上就是CommonLisp中解析命令行参数示例的详细内容,更多关于CommonLisp命令行参数的资料请关注易采站长站其它相关文章!