An opinionated command line parsing utility for Scala.
libraryDependencies += "io.crashbox" %% "commando" % "<latest_version>"
Commando is available for Scala, Scala JS and Scala Native.
Commando's API is designed around two main concepts: commands and parameters. It is recommended to read this section before diving into the Scala API.
Commands represent an action and have side effects when
run. Well-known examples include ls
, cp
, git
, etc.
On a single command line, only one command is ever invoked. However,
commands can be nested and share parameters. git
for example, fits
into this model:
git -p clone https://github.com/jodersky/commando
In the above, the top-level command has a parameter -p
, followed by
a "subcommand" clone
, which itself has a url as parameter. Typically,
a parent command will have several child commands. As such, a command
can also be thought of representing a on-of-many choice parameter.
Parameters define what arguments a command may take. There are two kinds of parameters: positional and optional.
Positional parameters simply get substituted by values from left to right.
E.g. assume a command command
that expects two positional parameters
x
followed by y
. Invoking the command as follows: command 1 2
will substitute 1 for x
and 2 for y
.
Optional parameters define arguments that may be passed to a command in any order. They typically represent "flags" or extra key-value information.
In order to avoid ambiguity with positional arguments, optionals must follow certain requiremenets:
-
Optionals start with
--
and may be positioned anywhere within the scope of a command (scope of a command are all words up to the next subcommand).E.g. in
command --foo <arg1> --bar subcommand --baz <arg2>
the optionals--foo
and--bar
refer tocommand
, and--baz
refers tosubcommand
. -
May have a single-character "short" alias, in which case they an be specified with a single dash. E.g.
--force
and-f
. -
Are always optional (use a command in case you need a one-of-many choice).
-
May be repeated.
command -v -v -v -v
-
May have embedded arguments after an equals sign.
command --publish=80:80
-
If specified, may allow or require an argument.
command --publish 80:80 --publish 443:443
-
In short form, and if they do not have parameters, arguments may be collapsed into a single string starting with a single dash. E.g.
command -i -t -f
is equivalent tocommand -itf
. -
A standalone double dash is an escape sequence. Any arguments following are treated as positionals. E.g.
ls -- --a-directory-starting-with---
.
The API is focused around a recursive
Command
object. This object
represents the "grammar" of a command line application, grouping
parameter definitions and subcommands.
It is passed to a command line parser, along with a sequence of arguments and is typically called from an application's entry point.
Commands may be constructed directly, however it is recommended to use the domain specific language that is provided in the commando package object.
For example, the following defines a subset of a docker-like command line utility:
import commando._
object Main {
val command = cmd("docker")(
opt("debug", 'D')
).sub(
cmd("run")(
opt("interactive", 'i'),
opt("tty", 't'),
pos("container")
).run{ arguments =>
// run container with arguments
println("running container " + arguments("container").head)
},
cmd("ps")(
opt("all", 'a')
).run{ arguments =>
if (arguments.contains("all")) {
// list all images
} else {
// list running containers
}
}
)
def main(args: Array[String]): Unit = commando.parse(args, command)
}
Assuming the application is packaged and executable as docker
, it
may be invoked as follows:
$ docker run -it my_container
running container my_container
$ docker -D ps --all