clingon
clingon is a Common Lisp command-line option parser that can easily parse command-line options with complex formats. For example, the following code prints a given number of hello messages
#!/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:
A little explanation. First execute the command ros init hello
to generate the prototype of the above code---load dependencies, package definitions, and empty functions main
. To load clingon, pass it as an argument to the function ql:quickload
. Then define a command
, handler
, and option
.
In clingon, instance objects of class clingon:command
represent a command that can be triggered in the shell, and they are created by function clingon:make-command
. Every command must have at least three elements:
-
:handler
, a function responsible for using command line options and implementing business logic; -
:name
, the name of the command, which is usually displayed in the command's usage description; -
:options
, the options accepted by this command.
此处的:handler
就是函数top-level/handler
,它会被函数---23f33bcdc272c77948836fe520326d0d clingon:run
(依赖注入的味道), clingon:command
object passed in. :options
currently only carries the definition of one option, namely
(clingon:make-option
:integer
:description "number of greetings"
:initial-value 1
:key :count
:long-name "count")
It defines an option whose value is an integer, specified on the command line by --count
. If this option is not passed in, the default value of 1 will be obtained when the function clingon:getopt
is used. If you want to get the value of this option from a command object, you need to call the function clingon:getopt
with the value of its :key
parameter as an argument, just like the function above top-level/handler
shown.
subcommand
clingon can also implement subcommand features such as git add
, git branch
. Subcommands like add
, branch
are still instances of class clingon:command
to clingon, but they are not passed to the function clingon:run
scheduling, but the parameter passed to the function clingon:make-command
:sub-command
, as shown in the following code
(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)))
Options and Parameters
The information passed to the process through the command line in clingon is divided into two forms: options and parameters. Options are referenced by name, while parameters are referenced by their subscripts. For example, in the first example, an option named --count
is defined, which is given the keyword :count
in the parsing result, which can be passed through the function clingon:getopt
to refer to its value; in contrast, the variable name
is the first of the remaining arguments after the option has been parsed from the command line, identified by position. clingon defines options through the function clingon:make-option
which provides a wealth of control capabilities.
option name
There are several names for the option, one is called :key
:long-name
which is the name used inside the program as one of the parameters of the function clingon:getopt
; :long-name
, generally a string with more than one character, such as "count"
, the name needs to be prefixed with two hyphens to use on the command line, such as --count 3
; the last one It is called :short-name
, which is a single character, such as #\v
, and is used with a hyphen prefix in the command line, such as -v
.
Necessity and Defaults
By passing the parameter :required t
to the function clingon:make-option
, an option can be required to be passed. For example, the options of the following command --n
are required
(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)))
If you don't want to tediously write --n 1
such command line parameters in some simplest cases, you can use :initial-value 1
to specify. In addition, you can also make the option read the value of the specified environment variable by default, use :env-vars
to specify the environment variable name.
(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)))
Options that can be used multiple times
Options like curl
-H
can be used multiple times, and an HTTP header can be added to the request each time it is specified, as shown in the following figure
In clingon, it can be achieved by passing in :list
to the function clingon:make-option
. When clingon:getopt
is used to take out the value of an option whose type is :list
, what is obtained is a list in which the strings of the input values are stored in sequence.
(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)))
Another case is when the same option is used multiple times despite having no value. For example, the option -v
of the command ssh
---, the more times it is used (up to 3 times), the more detailed debugging information will be printed by ssh
. This type of option is called :counter
in clingon.
(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)))
Signal options
There are some options that only need to distinguish between [yes] and [no], and do not need to care about the value of this option - or such options themselves are not allowed to have a value, such as docker run
command The options -d
and --detach
. The type of this option is :boolean/true
. If this option is specified, the value will always be t
. In contrast, the type :boolean/false
always takes the value 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)))
select option
If an option accepts a string, not all input is meaningful, such as option -T
for command dot
eec077afffce5e2790840924d6b3f2a4---. As can be seen from the man document of dot
, the image types it supports are limited, such as ps
, pdf
, png
etc. . Instead of declaring an option of type :string
, it is easier for clingon to check the validity of the input value on behalf of clingon. Here you can use the type :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)))
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。