diff --git a/404.html b/404.html index ac7b1f72..57e1718e 100755 --- a/404.html +++ b/404.html @@ -1940,7 +1940,7 @@
Note
+Aliases are not expanded recursively: none of the tokens that an alias expands to will be expanded again, even if they match another alias.
+You also use this functionality to implement command prefixes:
-Note that inferred names will always have a POSIX-style prefix like +
Note
+Inferred names will always have a POSIX-style prefix like
--name
. If you want to use a different prefix, you should specify all
option names manually.
Clikt has a large number of extension functions that can modify options. When applying multiple functions to the same option, there’s only one valid order for the functions to be applied. For @@ -3692,7 +3701,7 @@
validate()
class Database(name="db"): CliktCommand() {
+class Database: CliktCommand(name="db") {
override fun run() = Unit
}
@@ -2209,7 +2209,7 @@ Developing Command Lin
- Copyright © 2022 AJ Alt
+ Copyright © 2018 AJ Alt
diff --git a/search/search_index.json b/search/search_index.json
index 8f23afb8..32ce23b2 100755
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"Clikt (pronounced \u201cclicked\u201d) is a multiplatform Kotlin library that makes writing command line interfaces simple and intuitive. It\u2019s the \u201cCommand Line Interface for Kotlin\u201d.
It is designed to make the process of writing command line tools effortless while supporting a wide variety of use cases and allowing advanced customization when needed.
Clikt has:
- arbitrary nesting of commands
- composable, type safe parameter values
- generation of help output and shell autocomplete scripts
- multiplatform packages for JVM, Node.js, and native Linux, Windows and macOS
What does it look like? Here\u2019s a complete example of a simple Clikt program:
class Hello : CliktCommand() {\nval count: Int by option().int().default(1).help(\"Number of greetings\")\nval name: String by option().prompt(\"Your name\").help(\"The person to greet\")\n\noverride fun run() {\nrepeat(count) {\necho(\"Hello $name!\")\n}\n}\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
And here\u2019s what it looks like when run:
The help page is generated for you:
Errors are also taken care of:
"},{"location":"#installation","title":"Installation","text":"Clikt is distributed through Maven Central.
dependencies {\nimplementation(\"com.github.ajalt.clikt:clikt:4.2.1\")\n}\n
"},{"location":"#if-youre-using-maven-instead-of-gradle-use-artifactidclikt-jvmartifactid","title":"If you\u2019re using Maven instead of Gradle, use <artifactId>clikt-jvm</artifactId>
","text":""},{"location":"#multiplatform","title":"Multiplatform","text":"Clikt supports the following targets: jvm
, mingwX64
, linuxX64
, macosX64
, and js
(for both Node.js and Browsers). Artifacts for macosArm64 are also published, but not tested with CI. See the docs for more information about functionality supported on each target. You\u2019ll need to use Gradle 6 or newer.
"},{"location":"#snapshots","title":"Snapshots","text":"Snapshot builds are also available You'll need to add the Sonatype snapshots repository:
repositories {\nmaven {\nurl = uri(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n}\n}\n
"},{"location":"#api-reference","title":"API Reference","text":" - Commands and Exceptions
- Options
- Arguments
- Parameter Type Conversions
- Output Formatting
"},{"location":"advanced/","title":"Advanced Patterns","text":""},{"location":"advanced/#common-options-with-subcommands","title":"Common Options With Subcommands","text":"In some cases, you will have multiple subcommands that all share a common set of options. For example, you may have an option for a config file, or an output directory, or some API credentials. There are several ways to structure your commands to avoid repeating the option declarations in each subcommand.
"},{"location":"advanced/#defining-common-options-on-the-root-command","title":"Defining Common Options on the Root Command","text":"You can define your options on the root command and pass down the information via the context. With this design, you\u2019ll have to specify the common options before the subcommand name on the command line.
ExampleUsage 1Usage 2 class Config(val token: String, val hostname: String)\n\nclass MyApi : CliktCommand() {\nprivate val token by option(help=\"api token to use for requests\").default(\"...\")\nprivate val hostname by option(help=\"base url for requests\").default(\"example.com\")\n\noverride fun run() {\ncurrentContext.obj = Config(token, hostname)\n}\n}\n\nclass Store : CliktCommand() {\nprivate val file by option(help=\"file to store\").file(canBeDir = false)\nprivate val config by requireObject<Config>()\noverride fun run() {\nmyApiStoreFile(config.token, config.hostname, file)\n}\n}\n\nclass Fetch : CliktCommand() {\nprivate val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\nprivate val config by requireObject<Config>()\noverride fun run() {\nmyApiFetchFile(config.token, config.hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi --hostname=https://example.com store file.txt\n
$ ./myapi --hostname=https://example.com fetch --outdir=./out\n
"},{"location":"advanced/#defining-common-options-in-a-group","title":"Defining Common Options in a Group","text":"Instead of defining your common options on the root command, you can instead define them in an OptionGroup which you include in each subcommand. This allows you to specify all options after the subcommand name.
ExampleUsage 1Usage 2 class CommonOptions: OptionGroup(\"Standard Options:\") {\nval token by option(help=\"api token to use for requests\").default(\"...\")\nval hostname by option(help=\"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : CliktCommand() {\nprivate val commonOptions by CommonOptions()\nprivate val file by option(help=\"file to store\").file(canBeDir = false)\noverride fun run() {\nmyApiStoreFile(commonOptions.token, commonOptions.hostname, file)\n}\n}\n\nclass Fetch : CliktCommand() {\nprivate val commonOptions by CommonOptions()\nprivate val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\noverride fun run() {\nmyApiFetchFile(commonOptions.token, commonOptions.hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi store --hostname=https://example.com file.txt\n
$ ./myapi fetch --hostname=https://example.com --outdir=./out\n
"},{"location":"advanced/#defining-common-options-in-a-base-class","title":"Defining Common Options in a Base Class","text":"A third design to share options is to define the common options in a base class that all the subcommands inherit from.
ExampleUsage 1Usage 2 abstract class MyApiSubcommand : CliktCommand() {\nval token by option(help = \"api token to use for requests\").default(\"...\")\nval hostname by option(help = \"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : MyApiSubcommand() {\nprivate val file by option(help = \"file to store\").file(canBeDir = false)\noverride fun run() {\nmyApiStoreFile(token, hostname, file)\n}\n}\n\nclass Fetch : MyApiSubcommand() {\nprivate val outdir by option(help = \"directory to store file in\").file(canBeFile = false)\noverride fun run() {\nmyApiFetchFile(token, hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi store --hostname=https://example.com file.txt\n
$ ./myapi fetch --hostname=https://example.com --outdir=./out\n
"},{"location":"advanced/#command-aliases","title":"Command Aliases","text":"Clikt allows commands to alias command names to sequences of tokens. This allows you to implement common patterns like allowing the user to invoke a command by typing a prefix of its name, or user-defined aliases like the way you can configure git to accept git ci
as an alias for git commit
.
To implement command aliases, override CliktCommand.aliases
in your command. This function is called once at the start of parsing, and returns a map of aliases to the tokens that they alias to.
To implement git-style aliases:
ExampleUsage 1Usage 2 class Repo : NoOpCliktCommand() {\n// You could load the aliases from a config file etc.\noverride fun aliases(): Map<String, List<String>> = mapOf(\n\"ci\" to listOf(\"commit\"),\n\"cm\" to listOf(\"commit\", \"-m\")\n)\n}\n\nclass Commit: CliktCommand() {\nval message by option(\"-m\").default(\"\")\noverride fun run() {\necho(\"Committing with message: $message\")\n}\n}\n\nfun main(args: Array<String>) = Repo().subcommands(Commit()).main(args)\n
$ ./repo ci -m 'my message'\nCommitting with message: my message\n
$ ./repo cm 'my message'\nCommitting with message: my message\n
Note that aliases are not expanded recursively: none of the tokens that an alias expands to will be expanded again, even if they match another alias.
You also use this functionality to implement command prefixes:
ExampleUsage class Tool : NoOpCliktCommand() {\noverride fun aliases(): Map<String, List<String>> {\nval prefixCounts = mutableMapOf<String, Int>().withDefault { 0 }\nval prefixes = mutableMapOf<String, List<String>>()\nfor (name in registeredSubcommandNames()) {\nif (name.length < 3) continue\nfor (i in 1..name.lastIndex) {\nval prefix = name.substring(0..i)\nprefixCounts[prefix] = prefixCounts.getValue(prefix) + 1\nprefixes[prefix] = listOf(name)\n}\n}\nreturn prefixes.filterKeys { prefixCounts.getValue(it) == 1 }\n}\n}\n\nclass Foo: CliktCommand() {\noverride fun run() {\necho(\"Running Foo\")\n}\n}\n\nclass Bar: CliktCommand() {\noverride fun run() {\necho(\"Running Bar\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Foo(), Bar()).main(args)\n
$ ./tool ba\nRunning Bar\n
"},{"location":"advanced/#token-normalization","title":"Token Normalization","text":"To prevent ambiguities in parsing, aliases are only supported for command names. However, there\u2019s another way to modify user input that works on more types of tokens. You can set a tokenTransformer
on the command\u2019s context that will be called for each option and command name that is input. This can be used to implement case-insensitive parsing, for example:
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext { tokenTransformer = { it.lowercase() } }\n}\n\nval name by option()\noverride fun run() = echo(\"Hello $name!\")\n}\n
$ ./hello --NAME=Clikt\nHello Clikt!\n
"},{"location":"advanced/#replacing-stdin-and-stdout","title":"Replacing stdin and stdout","text":"By default, functions like CliktCommand.main
and option().prompt()
read from stdin and write to stdout. If you want to use Clikt in an environment where the standard streams aren\u2019t available, you can set your own implementation of a TerminalInterface
when customizing the command context.
object MyInterface : TerminalInterface {\noverride val info: TerminalInfo\nget() = TerminalInfo(/* ... */)\n\noverride fun completePrintRequest(request: PrintRequest) {\nif (request.stderr) MyOutputStream.writeError(request.text)\nelse MyOutputStream.write(request.text)\n}\n\noverride fun readLineOrNull(hideInput: Boolean): String? {\nreturn if (hideInput) MyInputStream.readPassword()\nelse MyInputStream.readLine()\n}\n}\n\nclass CustomCLI : NoOpCliktCommand() {\ninit { context { terminal = Terminal(terminalInterface = MyInterface ) } }\n}\n
Tip
If you want to log the output, you can use Mordant\u2019s TerminalRecorder
. That\u2019s how test is implemented!
"},{"location":"advanced/#command-line-argument-files-argfiles","title":"Command Line Argument Files (\u201c@argfiles\u201d)","text":"Similar to javac
, Clikt supports loading command line parameters from a file using the \u201c@argfile\u201d syntax. You can pass any file path to a command prefixed with @
, and the file will be expanded into the command line parameters. This can be useful on operating systems like Windows that have command line length limits.
If you create a file named cliargs
with content like this:
--number 1\n--name='jane doe' --age=30\n./file.txt\n
You can call your command with the contents of the file like this:
$ ./tool @cliargs\n
Which is equivalent to calling it like this:
$ ./tool --number 1 --name='jane doe' --age=30 ./file.txt\n
You can use any file path after the @
, and can specify multiple @argfiles:
$ ./tool @../config/args @C:\\\\Program\\ Files\\\\Tool\\\\argfile\n
If you have any options with names that start with @
, you can still use @argfiles
, but values on the command line that match an option will be parsed as that option, rather than an @argfile
, so you\u2019ll have to give your files a different name.
"},{"location":"advanced/#preventing-argfile-expansion","title":"Preventing @argfile expansion","text":"If you want to use a value starting with @
as an argument without expanding it, you have three options:
- Pass it after a
--
, which disables expansion for everything that occurs after it. - Escape it with
@@
. The first @
will be removed and the rest used as the argument value. For example, @@file
will parse as the string @file
- Disable @argfile expansion entirely by setting
Context.expandArgumentFiles = false
"},{"location":"advanced/#file-format","title":"File format","text":" - Normal shell quoting and escaping rules apply.
- Line breaks are treated as word separators, and can be used where you would normally use a space to separate parameters.
- Line breaks can occur within quotes, and will be included in the quoted value.
- @argfiles can contain other @argfile arguments, which will be expanded recursively.
- An unescaped
#
character outside of quotes is treated as a line comment: it and the rest of the line are skipped. You can pass a literal #
by escaping it with \\#
or quoting it with '#'
. - If a
\\
occurs at the end of a line, the next line is trimmed of leading whitespace and the two lines are concatenated.
"},{"location":"advanced/#custom-exit-status-codes","title":"Custom exit status codes","text":"Clikt will normally exit your program with a status code of 0 for a normal execution, or 1 if there\u2019s an error. If you want to use a different value, you can throw ProgramResult(statusCode)
. If you use CliktCommand.main
, that exception will be caught and exitProcess
will be called with the value of statusCode
.
You could also call exitProcess
yourself, but the ProgramResult has a couple of advantages:
ProgramResult
is easier to test. Exiting the process makes unit tests difficult to run. ProgramResult
works on all platforms. exitProcess
is only available on the JVM.
"},{"location":"advanced/#multiplatform-support","title":"Multiplatform Support","text":"Clikt supports the following platforms in addition to JVM:
"},{"location":"advanced/#desktop-native-linux-windows-and-macos","title":"Desktop native (Linux, Windows, and macOS)","text":"All functionality is supported, except:
env
parameter of editText and editFile is ignored. - file and path parameter types are not supported.
"},{"location":"advanced/#nodejs","title":"NodeJS","text":"All functionality is supported, except:
- file and path parameter types are not supported.
"},{"location":"advanced/#browser-javascript","title":"Browser JavaScript","text":"All functionality is supported, except:
- The default terminal only outputs to the browser\u2019s developer console, which is probably not what you want. You can define your own TerminalInterface, or you can call parse instead of main and handle output yourself.
- editText and editFile are not supported.
- file and path parameter types are not supported.
"},{"location":"arguments/","title":"Arguments","text":"Arguments are declared and customized similarly to options, but are provided on the command line positionally instead of by name. Arguments are declared with argument()
, and the order that they are declared defines the order that they must be provided on the command line.
"},{"location":"arguments/#basic-arguments","title":"Basic Arguments","text":"By default, argument
takes a single String
value which is required to be provided on the command line.
ExampleUsage class Hello : CliktCommand() {\nval name by argument()\noverride fun run() {\necho(\"Hello $name!\")\n}\n}\n
$ ./hello Foo\nHello Foo!\n
Arguments appear in the usage string, but listed in the help page unless you set their help
value. It\u2019s usually clearer to document arguments in the command help.
ExampleHelp Output class Cp : CliktCommand(\nhelp = \"Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\"\n) {\nprivate val source by argument().file(mustExist = true).multiple()\nprivate val dest by argument().file()\noverride fun run() {\n// ...\n}\n}\n
Usage: cp [<options>] [<source>]... <dest>\n\n Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"arguments/#variadic-arguments","title":"Variadic Arguments","text":"Like options, arguments can take any fixed number of values, which you can change with functions like pair
and triple
. Unlike options, arguments can also take a variable (or unlimited) number of values. This is common with file path arguments, since they are frequently expanded with a glob pattern on the command line.
Variadic arguments are declared with multiple
. You can declare any number of arguments with fixed numbers of values, but only one variadic argument in a command.
ExampleUsage class Copy : CliktCommand() {\nval source: List<Path> by argument().path(mustExist = true).multiple()\nval dest: Path by argument().path(canBeFile = false)\noverride fun run() {\necho(\"Copying files $source to $dest\")\n}\n}\n
$ ./copy file.* out/\nCopying files [file.txt, file.md] to out/\n
You can also use unique
to discard duplicates:
val source: Set<Path> by argument().path(mustExist = true).multiple().unique()\n
"},{"location":"arguments/#option-like-arguments-using-","title":"Option-Like Arguments (Using --
)","text":"Clikt normally parses any value that starts with punctuation as an option, which allows users to intermix options and arguments. However, sometimes you need to pass a value that starts with punctuation to an argument. For example, you might have a file named -file.txt
that you want to use as an argument.
Clikt supports the POSIX convention of using --
to force all following values to be treated as arguments. Any values before the --
will be parsed normally.
ExampleUsage 1Usage 2 class Touch : CliktCommand() {\nval verbose by option().flag()\nval files by argument().multiple()\noverride fun run() {\nif (verbose) echo(files.joinToString(\"\\n\"))\n}\n}\n
$ ./touch --foo.txt\nUsage: touch [<options>] [<files>]...\n\nError: no such option: \"--foo.txt\".\n
$ ./touch --verbose -- --foo.txt bar.txt\n--foo.txt\nbar.txt\n
"},{"location":"autocomplete/","title":"Shell Autocomplete","text":"Clikt includes built-in support for generating autocomplete scripts for bash, zsh and fish shells.
Example $ ./repo <TAB><TAB>\ncommit clone pull\n\n$ ./repo -<TAB>\n--config -h --help --repo-home --verbose\n\n$./repo --repo-home ./g<TAB>\n./git ./got ./good\n
"},{"location":"autocomplete/#enabling-completion","title":"Enabling Completion","text":"Clikt handles autocomplete by generating a shell script that defines the completion. You generate the script once each time your CLI changes, and load it each time your start your shell.
"},{"location":"autocomplete/#with-an-environment-variable","title":"With an environment variable","text":"You can generate the completion script by invoking your program with a special environment variable.
You can set the variable name manually with the autoCompleteEnvvar
parameter in the CliktCommand
constructor. By default, it\u2019s your command\u2019s name capitalized, with -
replaced with _
, and prefixed with another _
. So if your command name is my-command
, the variable would be _MY_COMMAND_COMPLETE=bash
, _MY_COMMAND_COMPLETE=zsh
, or _MY_COMMAND_COMPLETE=fish
, depending on your current shell.
For example to activate bash autocomplete for this command:
class MyProgram: CliktCommand() {\n// ...\n}\n
You can generate the completion script and save it to a file like this:
$ _MY_PROGRAM_COMPLETE=bash ./my-program > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#with-an-option","title":"With an option","text":"If you\u2019d prefer not to use environment variables, you can add a special option to your command with the completionOption
function. Invoking your program with this option will generate the completion script:
Example 1Example 2Usage class MyCommand: CliktCommand() {\ninit {\ncompletionOption()\n}\n// ...\n}\n
class MyCommand: CliktCommand() {\n//..\n}\n\nfun main(args: Array<String>) = MyCommand().completionOption().main(args)\n
$ ./my-command --generate-completion=bash > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#with-a-subcommand","title":"With a subcommand","text":"A third option is to add a subcommand that will generate the completion when invoked.
Example 1Example 2Usage class MyCommand: CliktCommand() {\ninit {\nsubcommands(CompletionCommand())\n}\n// ...\n}\n
class MyCommand: CliktCommand() {\n//..\n}\n\nfun main(args: Array<String>) = MyCommand().subcommands(CompletionCommand()).main(args)\n
$ ./my-command generate-completion bash > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#using-the-generated-script","title":"Using the generated script","text":"Once you\u2019ve generated the completion script, source the file to activate completion:
$ source ~/my-program-completion.sh\n
You can add that source command to your startup script so that completion is always available. For example, with bash:
$ echo source ~/my-program-completion.sh >> ~/.bashrc\n
You\u2019ll need to regenerate the completion script any time your command structure changes.
"},{"location":"autocomplete/#supported-functionality","title":"Supported Functionality","text":""},{"location":"autocomplete/#bash-and-zsh","title":"Bash and Zsh","text":"Currently subcommand, option, and command alias names can be completed, as well as values for options and arguments. choice
parameters are completed with their possible values. Other parameter types are completed as file or directory names. Context.allowInterspersedArgs
is supported.
"},{"location":"autocomplete/#fish","title":"Fish","text":"Fish\u2019s completion mechanism is more limited that Bash\u2019s. Subcommands can be completed, options can be completed as long as they start with a -
. Completion suggestions for positional arguments are the union of all positional arguments. Other advanced Clikt features are not supported.
"},{"location":"autocomplete/#customizing-completions","title":"Customizing Completions","text":"There is built-in completion for values for choice
parameters, and for parameters converted with file
and path
.
You can add completion for other parameters with the completionCandidates
parameter to option()
and argument()
. The value can be one of the following:
None
: The default. The parameter\u2019s values will not be completed. Path
: Completions will be filesystem paths. Hostname
: Completions will be read from the system\u2019s hosts file. Username
: Completions will be taken from the system\u2019s users. Fixed
: Completions are given as a fixed set of strings. Custom
: Completions are generated from a custom script.
"},{"location":"autocomplete/#custom-completion-candidates","title":"Custom
completion candidates","text":"The Custom
type takes a block that returns code to add to the script which generates completions for the given parameter.
If you just want to call another script or binary that prints all possible completion words to stdout, you can use fromStdout.
Both Bash and ZSH scripts use Bash\u2019s Programmable Completion system (ZSH via a comparability layer). The string returned from [generator] should be the body of a function that will be passed to compgen -F
.
Specifically, you should set the variable COMPREPLY
to the completion(s) for the current word being typed. The word being typed can be retrieved from the COMP_WORDS
array at index COMP_CWORD
.
Example with fromStdoutExample with full script class Hello: CliktCommand() {\n// This example uses `echo`, but you would use your own binary\n// or script that prints the completions.\nval name by option(completionCandidates =\nCompletionCandidates.Custom.fromStdout(\"echo completion1 completion2\")\n)\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
class Hello: CliktCommand() {\n// This is identical to the previous example\nval name by option(completionCandidates = CompletionCandidates.Custom {\n\"\"\"\n WORDS=${'$'}(echo completion1 completion2)\n COMPREPLY=(${'$'}(compgen -W \"${'$'}WORDS\" -- \"${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}\"))\n \"\"\".trimIndent()\n})\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
"},{"location":"autocomplete/#limitations","title":"Limitations","text":"Token Normalization is not supported.
If you have arguments that occur after a multiple
argument, those arguments won\u2019t be autocompleted. Partial command lines are ambiguous in those situations, and Clikt assumes that you\u2019re trying to complete the multiple
argument rather than the later ones.
Bash must be at least version 3, or Zsh must be at least version 4.1.
"},{"location":"changelog/","title":"Change Log","text":""},{"location":"changelog/#421","title":"4.2.1","text":""},{"location":"changelog/#added","title":"Added","text":" - Added
toString
implementations to options and arguments. (#434) - Added
CliktCommand.test
overload that takes a vararg of String
s as the command line arguments. Thanks to @sschuberth for the contribution (#451)
"},{"location":"changelog/#fixed","title":"Fixed","text":" - Update Mordant dependency to fix crashes on native targets and GraalVM (#447)
"},{"location":"changelog/#420","title":"4.2.0","text":""},{"location":"changelog/#added_1","title":"Added","text":" - Added
requireConfirmation
parameter to option().prompt()
(#426) - Added
CliktCommand.terminal
extension for accessing the terminal from a command. - Added
includeSystemEnvvars
, ansiLevel
, width
, and height
parameters to all CliktCommand.test
overloads.
"},{"location":"changelog/#deprecated","title":"Deprecated","text":" - Deprecated
CliktCommand.prompt
, use CliktCommand.terminal.prompt
or Prompt
instead. - Deprecated
CliktCommand.confirm
, use YesNoPrompt
instead.
"},{"location":"changelog/#fixed_1","title":"Fixed","text":" - Fixed incorrect error message when a
defaultLazy
option referenced a required
option. (#430)
"},{"location":"changelog/#410","title":"4.1.0","text":""},{"location":"changelog/#added_2","title":"Added","text":" - Added
MordantHelpFormatter.renderAttachedOptionValue
that you can override to change how option values are shown, e.g. if you want option to show as --option <value>
instead of --option=<value>
. (#416) - Added
option().optionalValueLazy{}
, which work like optionalValue()
but the default value is computed lazily. (#381)
"},{"location":"changelog/#changed","title":"Changed","text":" - Updated Kotlin to 1.9.0
PrintMessage
, PrintHelpMessage
and PrintCompletionMessage
now default to exiting with a status code 0, which is the behavior they had in 3.x. (#419)
"},{"location":"changelog/#400","title":"4.0.0","text":""},{"location":"changelog/#added_3","title":"Added","text":" - Added
Context.errorEncountered
which is true if parsing has continued after an error was encountered. option().help{\"\"}
and argument().help{\"\"}
extensions that set the parameter\u2019s help text lazily, with access to the current context so that you can add colors.
"},{"location":"changelog/#changed_1","title":"Changed","text":" Option.optionHelp
and Argument.argumentHelp
, CliktCommand.commandHelp
, and CliktCommand.commandHelpEpilog
are now methods that take the context as an argument, and the help
parameter to copy
is now a helpGetter
lambda. CliktCommand.shortHelp
now takes the context as an argument. - The
message
method on TransformContext
interfaces is now an extension.
"},{"location":"changelog/#deprecated_1","title":"Deprecated","text":" - Deprecated
CliktCommand.commandHelp
and commandHelpEpilog
properties in favor of the methods with the same name.
"},{"location":"changelog/#400-rc","title":"4.0.0-RC","text":""},{"location":"changelog/#added_4","title":"Added","text":" - You can now use markdown in your help strings, including tables and lists. Clikt uses the Mordant library for rendering.
- Help output and error messages now include colors by default. You can disable this or customize the styling by configuring the
context.terminal
- Added
Option.varargValues()
to create an option that accepts a variable number of values - Added
Option.optionalValue()
to create an option whose value is optional. - Added
obj
setter to context builder as an alternative to currentContext.obj
- Added
boolean()
parameter type conversions. - Added
uint()
and ulong()
parameter type conversions. - Added
nullableFlag()
parameter transformation. - Added
CliktCommand.test
extension for testing your commands and their output - Clikt will now report multiple errors if they occur via the new
MultiUsageError
exception, rather than just reporting the first error. (#367) - Added
CliktCommand.allHelpParams()
, which can be overridden to change which parameters are displayed in help output - Added
Context.argumentFileReader
which allows custom loading of argument files - Added
Context.allowGroupedShortOptions
which can disable parsing -abc
as -a -b -c
- Options named
-?
or /?
are now supported - Added
option(eager=true)
to create an eager option that takes values - Added
option(acceptsUnattachedValue=false)
to force the option to only accept values like --option=1
and not --option 1
- Added
CliktCommand.test()
that captures the output of a command and does not exit the process.
"},{"location":"changelog/#removed","title":"Removed","text":" - Removed
CliktConsole
. Mordant is now used for all input and output. If you were defining a custom console, instead define a mordant TerminalInterface
and set it on your context\u2019s Terminal
. - Removed
TermUi.echo
, TermUi.prompt
, and TermUi.confirm
. Use the equivalent methods on your CliktCommand
, or use mordant\u2019s prompts directly. - Removed legacy JS publications. Now only the JS/IR artifacts are published.
- Removed
CliktHelpFormatter
. Use MordantHelpFormatter
instead. - Removed
FlagOption
and EagerOption
classes. All options are now implemented as transformations on OptionWithValues
. FlagOption
is now OptionWithValues<Boolean, Boolean, Boolean>
.
"},{"location":"changelog/#changed_2","title":"Changed","text":" prompt
and confirm
are now implemented with mordant\u2019s prompt functionality, and the method parameters have changed to match mordant\u2019s - When using
treatUnknownOptionsAsArgs
, grouped short options like -abc
will be treated as an argument rather than reporting an error as long as they don\u2019t match any short options in the command. (#340) - Clikt no longer automatically calls
trimIndent
on strings passed to help
. Call trimIndent
or trimMargin
yourself if necessary. Context.Builder.helpOptionNames
now accepts any iterable rather than just a set. CliktCommand.echo
and prompt
are now public. (#407) - Internally, all options are implemented transformations on
OptionWithValues
, rather than using separate classes for each option type. - Some Localization strings have changed, removed
Localization.aborted()
, added Localization.argumentsMetavar()
Context.Builder.helpFormatter
is now a lambda that takes the current context as an argument - Exceptions have been reworked so that all exceptions thrown by Clikt are subclasses of
CliktError
. CliktError
now includes statusCode
and printError
properties. - The constructor of
UsageError
and its subclasses no longer takes a context
parameter. The context is now inferred automatically. UsageError.formatUsage
now takes the localization and formatter as arguments
"},{"location":"changelog/#fixed_2","title":"Fixed","text":" - When parsing a command line with more than one error, Clikt will now always report the error that occurs earliest if it can\u2019t report them all (#361)
- When
treatUnknownOptionsAsArgs
is true, grouped unknown short options will now be treated as arguments rather than reporting an error.
"},{"location":"changelog/#354","title":"3.5.4","text":""},{"location":"changelog/#fixed_3","title":"Fixed","text":" - Revert jvm jars to target Java 8
"},{"location":"changelog/#353","title":"3.5.3","text":""},{"location":"changelog/#changed_3","title":"Changed","text":" - Updated Kotlin to 1.8.22
"},{"location":"changelog/#fixed_4","title":"Fixed","text":" - Context is now set properly on NoSuchOption exceptions when thrown from subcommands. (#399)
- When
treatUnknownOptionsAsArgs
is true, grouped unknown short options will now be treated as arguments rather than reporting an error.
"},{"location":"changelog/#352","title":"3.5.2","text":""},{"location":"changelog/#changed_4","title":"Changed","text":" - Updated Kotlin to 1.8.10
"},{"location":"changelog/#fixed_5","title":"Fixed","text":" - Fix
CliktCommand.prompt
on NodeJS targets that would hang due to KT-55817 (#387)
"},{"location":"changelog/#351","title":"3.5.1","text":""},{"location":"changelog/#changed_5","title":"Changed","text":" - Updated Kotlin to 1.7.20
"},{"location":"changelog/#fixed_6","title":"Fixed","text":" - Support unicode in environment variable values on Native Windows. (#362)
- Support environment variables for options in a mutually exclusive options group. (#384)
"},{"location":"changelog/#350","title":"3.5.0","text":""},{"location":"changelog/#added_5","title":"Added","text":" - Added
hidden
parameter to CliktCommand
, which will prevent the command from being displayed as a subcommand in help output (#353) - Publish artifacts for the
macosArm64
target. Note that this target is not tested on CI. (#352)
"},{"location":"changelog/#changed_6","title":"Changed","text":" - Default values for arguments will now be included in help output when
showDefaultValues=true
is set on your help formatter (#357)
"},{"location":"changelog/#fixed_7","title":"Fixed","text":" - Fix flags and other options with defaults not being usable in
mutuallyExclusiveOptions
(#349) - Fix
CompletionCommand
generating completion for itself (#355)
"},{"location":"changelog/#342","title":"3.4.2","text":""},{"location":"changelog/#deprecated_2","title":"Deprecated","text":" TermUi.echo
, TermUi.prompt
, and TermUi.confirm
. Use the equivalent methods on CliktCommand
instead. (#344)
"},{"location":"changelog/#341","title":"3.4.1","text":""},{"location":"changelog/#added_6","title":"Added","text":" - Added
obj
setter to context builder as an alternative to currentContext.obj
- Added
option().boolean()
and argument().boolean()
uint()
and ulong()
parameter type conversions. CliktCommand.test
extension for testing your commands and their output
"},{"location":"changelog/#changed_7","title":"Changed","text":" - Updated Kotlin to 1.6.20
"},{"location":"changelog/#340","title":"3.4.0","text":""},{"location":"changelog/#changed_8","title":"Changed","text":" unique()
now works with any option with a list type, not just multiple()
options (#332) - Updated Kotlin to 1.6.10
"},{"location":"changelog/#fixed_8","title":"Fixed","text":" - Fixed co-occurring option groups returning null when all options in the group are defined in environment variables (#330)
"},{"location":"changelog/#330","title":"3.3.0","text":""},{"location":"changelog/#added_7","title":"Added","text":" - Added
default
parameter to argument().multiple()
(#305) Context.originalArgv
that allows you to read the command line arguments from within a command\u2019s run
(#290) context { envarReader = {...} }
to set a custom function to read from environment variables (#299)
"},{"location":"changelog/#changed_9","title":"Changed","text":" defaultLazy
values can now reference other parameters, as long the referenced parameters do not also reference other parameters - You can now call
CliktCommand.context
multiple times on the same command, and all builder blocks will be applied - Validate values entered to a
prompt
option, and show another prompt if the validation fails (#288) - Updated kotlin to 1.5.31
"},{"location":"changelog/#fixed_9","title":"Fixed","text":" - Report error when excess arguments are given to a command with
allowMultipleSubcommands=true
(#303)
"},{"location":"changelog/#320","title":"3.2.0","text":""},{"location":"changelog/#added_8","title":"Added","text":" InputStream.isCliktParameterDefaultStdin
and OutputStream.isCliktParameterDefaultStdout
to check if the streams returned from inputStream
/outputStream
options are proxying stdin/stdout (#272)
"},{"location":"changelog/#changed_10","title":"Changed","text":" - Make parameters of
mutuallyExclusiveOptions
covariant to allow validation without explicit type annotations. (#265) - Updated kotlin to 1.5.0
"},{"location":"changelog/#fixed_10","title":"Fixed","text":" - Reading from an option or argument property on a command that hasn\u2019t been invoked will now always throw an
IllegalStateException
"},{"location":"changelog/#310","title":"3.1.0","text":""},{"location":"changelog/#added_9","title":"Added","text":" - Added
required()
and defaultLazy()
for nullable flag options like switch()
. (#240) - Added support for generating autocomplete scripts for Fish shells (#189)
- Added
CompletionCommand
and CliktCommand.completionOption()
that will print an autocomplete script when invoked, as an alternative to using environment variables.
"},{"location":"changelog/#changed_11","title":"Changed","text":" - Updated Kotlin to 1.4.21
@argfiles
now allow line breaks in quoted values, which are included in the value verbatim. You can now end lines with \\
to concatenate them with the following line. (#248)
"},{"location":"changelog/#301","title":"3.0.1","text":""},{"location":"changelog/#deprecated_3","title":"Deprecated","text":" - Deprecated calling
echo
with err
or lineSeparator
but no message
.
"},{"location":"changelog/#300","title":"3.0.0","text":""},{"location":"changelog/#added_10","title":"Added","text":" - Clikt\u2019s JS target now supports both NodeJS and Browsers. (#198)
- Default values for switch options are now shown in the help. Help text can be customized using the
defaultForHelp
argument, similar to normal options. (#205) - Added
FlagOption.convert
(#208) - Added ability to use unicode NEL character (
\\u0085
) to manually break lines in help output (#214) - Added
help(\"\")
extension to options and arguments as an alternative to passing the help as an argument (#207) - Added
valueSourceKey
parameter to option
- Added
check()
extensions to options and arguments as an alternative to validate()
- Added
prompt
and confirm
functions to CliktCommand
that call the TermUi
equivalents with the current console. - Added
echo()
overload with no parameters to CliktCommand that prints a newline by itself. - Added localization support. You can set an implementation of the
Localization
interface on your context with your translations. (#227)
"},{"location":"changelog/#fixed_11","title":"Fixed","text":" - Hidden options will no longer be suggested as possible typo corrections. (#202)
- Options and Arguments with
multiple(required=true)
will now show as required in help output. (#212) - Multiple short lines in a help text paragraph no longer appear dedented (#215)
"},{"location":"changelog/#changed_12","title":"Changed","text":" - Updated Kotlin to 1.4.0
Argument.help
and Option.help
properties have been renamed to argumentHelp
and optionHelp
, respectively. The help
parameter names to option()
and argument()
are unchanged. commandHelp
and commandHelpEpilog
properties on CliktCommand
are now open
, so you can choose to override them instead of passing help
and epilog
to the constructor. - Replaced
MapValueSource.defaultKey
with ValueSource.getKey()
, which is more customizable. Option.metavar
, Option.parameterHelp
, OptionGroup.parameterHelp
and Argument.parameterHelp
properties are now functions. - Changed constructor parameters of
CliktHelpFormatter
. Added localization
and removed usageTitle
, optionsTitle
, argumentsTitle
, commandsTitle
, optionsMetavar
, and commandMetavar
. Those strings are now defined on equivalently named functions on Localization
.
"},{"location":"changelog/#removed_1","title":"Removed","text":" - Removed
envvarSplit
parameter from option()
and convert()
. Option values from environment variables are no longer split automatically. (#177) - Removed public constructors from the following classes:
ProcessedArgument
, OptionWithValues
, FlagOption
, CoOccurringOptionGroup
, ChoiceGroup
, MutuallyExclusiveOptions
. MissingParameter
exception replaced with MissingOption
and MissingArgument
- Removed
Context.helpOptionMessage
. Override Localization.helpOptionMessage
and set it on your context instead.
"},{"location":"changelog/#deprecated_4","title":"Deprecated","text":" @ExperimentalCompletionCandidates
and @ExperimentalValueSourceApi
annotations. These APIs no longer require an opt-in.
"},{"location":"changelog/#280","title":"2.8.0","text":""},{"location":"changelog/#added_11","title":"Added","text":" - Added
error
parameter to PrintMessage
and PrintHelpMessage
. When true
, CliktCommand.main
will exit with status code 1. (#187)
"},{"location":"changelog/#changed_13","title":"Changed","text":" - When
printHelpOnEmptyArgs
is true
and no arguments are present, or when invokeWithoutSubcommand
is false
and no subcommand is present, CliktCommand.main
will now exit with status code 1 rather than 0. restrictTo
now works with any Comparable
value, not just Number
. CliktCommand.main
now accepts Array<out String>
, not just Array<String>
. (#196)
"},{"location":"changelog/#fixed_12","title":"Fixed","text":" - Fixed option values being reset when calling multiple subcommands with
allowMultipleSubcommands=true
(#190)
"},{"location":"changelog/#271","title":"2.7.1","text":""},{"location":"changelog/#fixed_13","title":"Fixed","text":" - Fixed NPE thrown in some cases when using
defaultByName
(#179)
"},{"location":"changelog/#270","title":"2.7.0","text":""},{"location":"changelog/#added_12","title":"Added","text":" - Ability to use custom program exit status codes via
ProgramResult
. inputStream
and outputStream
conversions for options and arguments. (#157 and #159) splitPair
, toMap
, and associate
extensions on option
. (#166) treatUnknownOptionsAsArgs
parameter to CliktCommand
. (#152) defaultByName
function for groupChoice
and groupSwitch
options. (#171)
"},{"location":"changelog/#changed_14","title":"Changed","text":" - Update Kotlin to 1.3.71
- Improved command name inference. Now, a class like
MyAppCommand
will infer its commandName
as my-app
rather than myappcommand
. You can still specify the name manually as before. (#168)
"},{"location":"changelog/#fixed_14","title":"Fixed","text":" - Correctly parse short options with attached values that contain
=
"},{"location":"changelog/#260","title":"2.6.0","text":""},{"location":"changelog/#added_13","title":"Added","text":" registeredSubcommands
, registeredOptions
, registeredArguments
, and registeredParameterGroups
methods on CliktCommand
. - Ability to read default option values from configuration files and other sources. Support for Java property files is built in on JVM, see the
json
sample for an example of reading from other formats. allowMultipleSubcommands
parameter to CliktCommand
that allows you to pass multiple subcommands in the same call. (docs) - Errors from typos in subcommand names will now include suggested corrections. Corrections for options and subcommands are now based on a Jaro-Winkler similarity metric, and can be customized with
Context.correctionSuggestor
"},{"location":"changelog/#changed_15","title":"Changed","text":" - Update Kotlin to 1.3.70
convert
can be called more than once on the same option or argument, including after calls to conversion functions like int
and file
. CliktCommand.toString
now includes the class name - Reverted automatic
~
expansion in file()
and path()
introduced in 2.5.0. If you need this behavior, you can implement it with code like convert { /* expand tidle */ }.file()
"},{"location":"changelog/#deprecated_5","title":"Deprecated","text":" wrapValue
is now deprecated, since convert
can be used in its place instead.
"},{"location":"changelog/#250","title":"2.5.0","text":""},{"location":"changelog/#added_14","title":"Added","text":" - Clikt is now available as a Kotlin Multiplatform Project, supporting JVM, NodeJS, and native Windows, Linux, and macOS.
eagerOption {}
function to more easily register eager options. - Eager options can now be added to option groups in help out by passing a value for
groupName
when creating them. canBeSymlink
parameter to file()
and path()
conversions that can be used to disallow symlinks CliktCommand.eagerOption
to simplify creating custom eager options
"},{"location":"changelog/#changed_16","title":"Changed","text":" - The parameter names of
file()
and path()
conversions have changed. The existing names are deprecated, and can be converted to the new usages with an IntelliJ inspection. Note that if you are calling these functions with unnamed arguments (e.g. file(true, false)
), you\u2019ll need to add argument names in order to remove the deprecation warning.
"},{"location":"changelog/#deprecated_6","title":"Deprecated","text":" - The
CliktCommand.context
property has been deprecated in favor of the new name, currentContext
, to avoid confusion with the CliktCommand.context{}
method. NoRunCliktCommand
was renamed to NoOpCliktCommand
. The existing class is deprecated. (#130)
"},{"location":"changelog/#fixed_15","title":"Fixed","text":" file()
and path()
conversions will now properly expand leading ~
in paths to the home directory for mustExist
, canBeFile
, and canBeDir
checks. The property value is unchanged, and can still begin with a ~
. (#131)
"},{"location":"changelog/#240","title":"2.4.0","text":""},{"location":"changelog/#added_15","title":"Added","text":" CompletionCandidates.Fixed
now has a secondary convenience constructor that take a vararg
of String
s CompletionCadidates.Custom
, which allows you to call other binaries or write a script to generate completions. This class is currently experimental. (#79) Option.wrapValue
and Argument.wrapValue
to make it easier to reuse existing conversion functions. ignoreCase
parameter to choice()
and enum()
conversion functions.
"},{"location":"changelog/#changed_17","title":"Changed","text":" option()
and argument()
now take optional completionCandidates
parameters to override how completion is generated. The constructor and copy
functions of OptionsWithValues
and ProcessedArgument
have changed to support default values. - The overloads of
findObject
(1 2) that take a default value have been renamed findOrSetObject
. The existing names are marked with @Deprecated
, and IntelliJ can convert your call sites automatically. (#110) enum()
parameters now accept case-insensitive values by default. You change this behavior by passing ignoreCase = false
to enum()
(#115)
"},{"location":"changelog/#fixed_16","title":"Fixed","text":" groupChoice
help output now includes the choices in the help output metavar TermUi.edit*
functions could freeze on certain editors (#99, thanks @iampravikant and @sebokopter) - Shell completion can now handle command names with dashes. (#104)
- Arguments with
=
in them could be incorrectly interpreted as options (#106)
"},{"location":"changelog/#230","title":"2.3.0","text":""},{"location":"changelog/#added_16","title":"Added","text":" option().groupSwitch()
, which works like groupChoice()
, but uses a switch()
option rather than a choice()
option. UsageError
now has a statusCode
parameter (which defaults to 1). If you\u2019re using ClicktCommand.main
, the value of statusCode
will be passed to exitProcess
.
"},{"location":"changelog/#changed_18","title":"Changed","text":" - Shell completion code is now printed by throwing a
PrintCompletionMessage
(a subclass of PrintMessage
) rather than calling echo
directly.
"},{"location":"changelog/#220","title":"2.2.0","text":""},{"location":"changelog/#added_17","title":"Added","text":" - Added
enum()
conversion for options and arguments. (#84)
"},{"location":"changelog/#changed_19","title":"Changed","text":" - There are now several ways of preventing @-file expansion
"},{"location":"changelog/#fixed_17","title":"Fixed","text":" - Help output missing items when no help text is specified. (#85)
- Help output not grouping options in groups passed to
groupChoice
. (#88)
"},{"location":"changelog/#210","title":"2.1.0","text":""},{"location":"changelog/#added_18","title":"Added","text":" - Ability to prevent rewrapping individual paragraphs in help output.
- Added parameter
required
to Option.multiple()
to require at least one instance of the option on the command line.
"},{"location":"changelog/#changed_20","title":"Changed","text":" CliktCommand.toString()
now includes the names and values of all parameters and subcommands.
"},{"location":"changelog/#fixed_18","title":"Fixed","text":" - Create subcommand context when
helpOptionNames
is empty. (#64)
"},{"location":"changelog/#200","title":"2.0.0","text":""},{"location":"changelog/#added_19","title":"Added","text":" - Bash autocomplete script generation. A property named
completionCandidates
has been added to Argument
and Option
interfaces, and corresponding parameters have been added to the various implementation constructors, as well as the convert
functions. You can use this to control the values autocomplete that will be suggested. option().split()
, and the corresponding OptionWithValues.valueSplit
. - Marking options as deprecated with
option().deprecated()
- You can manually set the pattern to split envvars on by passing a pattern to the
envvarSplit
parameter of option()
- Option groups, mutually exclusive groups, co-occurring groups, and choice options with groups
- Support for Command line argument files a.k.a. \u201c@-files\u201d
"},{"location":"changelog/#changed_21","title":"Changed","text":" - If multiple
--
tokens are present on the command line, all subsequent occurrences after the first are now parsed as positional arguments. Previously, subsequent --
tokens were skipped. - The
PlaintextHelpFormatter
has been replaced with CliktHelpFormatter
, which is more customizable. See the docs for more info, or the new sample for an example of customizing help output to use ANSI colors. - Some of the properties and constructor parameters for
OptionWithValues
and ProcessedArgument
have changed. - The
OptionDelegate
interface has changed, and GroupableOption
and ParameterHolder
interfaces have been added to work with option groups. - Parameter validation now occurs after all parameter delegates have set their values, so the lambdas passed to
validate
may reference other parameters.
"},{"location":"changelog/#170","title":"1.7.0","text":""},{"location":"changelog/#added_20","title":"Added","text":" printHelpOnEmptyArgs
parameter to CliktCommand
constructor. (#41)
"},{"location":"changelog/#fixed_19","title":"Fixed","text":" - Usage errors now correctly print subcommand names. (#47)
- Arguments with
multiple(required=true)
now report an error if no argument is given on the command line. (#36)
"},{"location":"changelog/#160","title":"1.6.0","text":""},{"location":"changelog/#added_21","title":"Added","text":" .multiple().unique()
modifier for options and arguments.
"},{"location":"changelog/#fixed_20","title":"Fixed","text":" - Support multi-line input when redirecting stdin
"},{"location":"changelog/#150","title":"1.5.0","text":""},{"location":"changelog/#added_22","title":"Added","text":" - Ability to use alternate output streams rather than stdin and stdout by setting
Context.console
or by passing a console to TermUI
functions.
"},{"location":"changelog/#140","title":"1.4.0","text":""},{"location":"changelog/#added_23","title":"Added","text":" path()
type for parameter values
"},{"location":"changelog/#changed_22","title":"Changed","text":" - Clikt now targets JVM 8 bytecode
- Responses to
TermUi.confirm()
are now case-insensitive
"},{"location":"changelog/#130","title":"1.3.0","text":""},{"location":"changelog/#added_24","title":"Added","text":" defaultLazy
extension for options and arguments
"},{"location":"changelog/#changed_23","title":"Changed","text":" main
now prints messages to stderr instead of stdout
"},{"location":"changelog/#fixed_21","title":"Fixed","text":" - Parameter help messages are now wrapped more consistently
"},{"location":"changelog/#120","title":"1.2.0","text":""},{"location":"changelog/#added_25","title":"Added","text":" - Default parameter to
option().default()
"},{"location":"changelog/#changed_24","title":"Changed","text":" - Treat tokens with unknown prefixes as arguments (this makes it easier to pass in file paths without using
--
).
"},{"location":"changelog/#110","title":"1.1.0","text":""},{"location":"changelog/#added_26","title":"Added","text":" List<String>
overloads to CliktCommand.parse
and main
err
parameter to TermUi.echo
error
property to Abort
"},{"location":"commands/","title":"Commands","text":"Clikt supports arbitrarily nested commands. You can add one command as a child of another with the subcommands
function, which can be called either in an init
block, or on an existing instance.
"},{"location":"commands/#executing-nested-commands","title":"Executing Nested Commands","text":"For commands with no children, run
is called whenever the command line is parsed (unless parsing is aborted from an error or an option like --help
).
If a command has children, this isn\u2019t the case. Instead, its run
is called only if a child command is invoked, just before the subcommand\u2019s run
. If a parent command is called without specifying a subcommand, the help page is printed and run
is not called.
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\nval verbose by option().flag(\"--no-verbose\")\noverride fun run() {\necho(\"Verbose mode is ${if (verbose) \"on\" else \"off\"}\")\n}\n}\n\nclass Execute : CliktCommand() {\noverride fun run() {\necho(\"executing\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n --verbose / --no-verbose\n -h, --help Show this message and exit\n\nCommands:\n execute\n
$ ./tool --verbose execute\nVerbose mode is on\nexecuting\n
"},{"location":"commands/#customizing-command-name","title":"Customizing Command Name","text":"The default name for subcommands is inferred as a lowercase name from the command class name. You can also set a name manually in the CliktCommand
constructor.
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\noverride fun run()= Unit\n}\n\nclass Execute : CliktCommand(name = \"RUN-ME\") {\noverride fun run() {\necho(\"executing\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool RUN-ME\nexecuting\n
$ ./tool -h\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n RUN-ME\n
"},{"location":"commands/#passing-parameters","title":"Passing Parameters","text":"When calling subcommands, the position of options and arguments on the command line affect which command will parse them. A parameter is parsed by a command if it occurs after the command name, but before any other command names.
ExampleUsage class Tool : CliktCommand(help = \"A tool that runs\") {\nval verbose by option().flag(\"--no-verbose\")\noverride fun run() = Unit\n}\n\nclass Execute : CliktCommand(help = \"Execute the command\") {\nval name by option()\noverride fun run() = Unit\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool --help\nUsage: tool [<options>] <command> [<args>]...\n\n A tool that runs\n\nOptions:\n --verbose / --no-verbose\n -h, --help Show this message and exit\n\nCommands:\n execute Execute the command\n
If you instead execute --help
after the subcommand, the subcommand\u2019s help is printed:
$ ./tool execute --help\nUsage: execute [<options>]\n\n Execute the command\n\nOptions:\n --name <text>\n -h, --help Show this message and exit\n
But executing ./tool --help execute
, with the option before the subcommand, will cause the parent\u2019s help option to be invoked, printing out Tool
\u2018s help page as if you just typed ./tool --help
.
"},{"location":"commands/#nested-handling-and-contexts","title":"Nested Handling And Contexts","text":"Normally nested command are independent of each other: a child can\u2019t access its parent\u2019s parameters. This makes composing commands much easier, but what if you want to pass information to a child command? You can do so with the command\u2019s Context
.
Every time the command line is parsed, each command creates a new context object for itself that is linked to its parent\u2019s context. Context
objects have a number of properties that can be used to customize command line parsing. Although each command creates its own context, the configuration is inherited from the parent context.
Context
objects also have an obj
property that can hold an object that can be accessed from child commands.
ExampleUsage class Tool : CliktCommand() {\nval verbose by option().flag(\"--no-verbose\")\nval config by findOrSetObject { mutableMapOf<String, String>() }\noverride fun run() {\nconfig[\"VERBOSE\"] = if (verbose) \"on\" else \"off\"\n}\n}\n\nclass Execute : CliktCommand() {\nval config by requireObject<Map<String, String>>()\noverride fun run() {\necho(\"Verbose mode is ${config[\"VERBOSE\"]}\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool --verbose execute\nVerbose mode is on\n
The findObject
, findOrSetObject
, and requireObject
functions will walk up the context tree until they find a obj
with the given type. If no such object exists, they will either return null
, throw an exception, or create an instance of the object and store it on the command\u2019s context, depending on which function you use. Since each context only has a single obj
, if you need to store multiple objects on a single context, you could create a data class with everything you want to store and set that as your obj
.
Note that the findOrSetObject
property is lazy and won\u2019t set the Context\u2019s obj
until its value is accessed. If you need to set an object for subcommands without accessing the property, you should use currentContext.findOrSetObject
, or set currentContext.obj
or Context.Builder.obj
directly, instead.
Eager initialization with findOrSetObjectEager initialization with currentContext.objEager initialization with context builder class Tool : CliktCommand() {\noverride fun run() {\n// runs eagerly\ncurrentContext.findOrSetObject { MyConfig() }\n}\n}\n
class Tool : CliktCommand() {\noverride fun run() {\n// runs eagerly, won't look for parent contexts\ncurrentContext.obj = MyConfig()\n}\n}\n
Tool().context {\n// runs eagerly, won't look for parent contexts\nobj = MyConfig()\n}\n
"},{"location":"commands/#running-parent-command-without-children","title":"Running Parent Command Without Children","text":"Normally, if a command has children, run
is not called unless a child command is invoked on the command line. Instead, --help
is called on the parent. If you want to change this behavior to always call run()
on the parent, you can do so by setting invokeWithoutSubcommand
to true
. The Context
will then have information on the subcommand that is about to be invoked, if there is one.
ExampleUsage 1Usage 2 class Tool : CliktCommand(invokeWithoutSubcommand = true) {\noverride fun run() {\nval subcommand = currentContext.invokedSubcommand\nif (subcommand == null) {\necho(\"invoked without a subcommand\")\n} else {\necho(\"about to run ${subcommand.commandName}\")\n}\n}\n}\n\nclass Execute : CliktCommand() {\noverride fun run() {\necho(\"running subcommand\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool\ninvoked without a subcommand\n
$./tool execute\nabout to run execute\nrunning subcommand\n
"},{"location":"commands/#customizing-contexts","title":"Customizing Contexts","text":"Contexts have a number of properties that can be customized, and which are inherited by child commands. You can change these properties with the context
builder function, which can be called in an init
block, or on a command instance.
For example, you can change the name of help option. These definitions are equivalent:
Version 1Version 2Usage class Cli : NoOpCliktCommand() {\ninit {\ncontext { helpOptionNames = setOf(\"/help\") }\n}\n}\nfun main(args: Array<String>) = Cli()\n
class Cli : NoOpCliktCommand()\nfun main(args: Array<String>) = Cli()\n.context { helpOptionNames = setOf(\"/help\") }\n.main(args)\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n -h, --help print the help\n
"},{"location":"commands/#printing-the-help-message-when-no-arguments-are-given","title":"Printing the Help Message When No Arguments Are Given","text":"Normally, if a command is called with no values on the command line, a usage error is printed if there are required parameters, or run
is called if there aren\u2019t any.
You can change this behavior by passing printHelpOnEmptyArgs = true
to your command\u2019s constructor. This will cause a help message to be printed when no values are provided on the command line, regardless of the parameters in your command.
ExampleUsage class Cli : CliktCommand(printHelpOnEmptyArgs = true) {\nval arg by argument()\noverride fun run() { echo(\"Command ran\") }\n}\n
$ ./cli\nUsage: cli [<options>]\n\nOptions:\n -h, --help print the help\n
"},{"location":"commands/#warnings-and-other-messages","title":"Warnings and Other Messages","text":"When you want to show information to the user, you\u2019ll usually want to use the functions for printing to stdout directly.
However, there\u2019s another mechanism that can be useful when writing reusable parameter code: command messages. These messages are buffered during parsing and printed all at once immediately before a command\u2019s run
is called. They are not printed if there are any errors in parsing. This type of message is used by Clikt for deprecating options
.
You can issue a command message by calling CliktCommand.issueMessage
or with the message
function available in the context of parameter transformers.
ExampleUsage 1Usage 2 class Cli : CliktCommand() {\n// This will print the warning when the option is given, but not if there are errors\nval opt by option().validate {\nif (it.isEmpty()) message(\"Empty strings are not recommended\")\n}\noverride fun run() {\necho(\"command run\")\n}\n}\n
$ ./cli --opt=''\nEmpty strings are not recommended\ncommand run\n
$ ./cli --opt='' --oops\nError: no such option: \"--oops\".\n
You can disable automatic message printing on the command\u2019s context:
ExampleUsage class Cli : CliktCommand() {\ninit { context { printExtraMessages = false } }\nval opt by option().validate {\nif (it.isEmpty()) message(\"Empty strings are not recommended\")\n}\noverride fun run() {\necho(\"command run\")\n}\n}\n
$ ./cli --opt=''\ncommand run\n
"},{"location":"commands/#chaining-and-repeating-subcommands","title":"Chaining and Repeating Subcommands","text":"Some command line interfaces allow you to call more than one subcommand at a time. For example, you might do something like gradle clean build publish
to run the clean
task, then the build
task, then the publish
task, which are all subcommands of gradle
.
To do this with Clikt, pass allowMultipleSubcommands = true
to your CliktCommand
constructor.
ExampleUsage class Compiler: CliktCommand(allowMultipleSubcommands = true) {\noverride fun run() {\necho(\"Running compiler\")\n}\n}\n\nclass Clean: CliktCommand() {\nval force by option().flag()\noverride fun run() {\necho(\"Cleaning (force=$force)\")\n}\n}\n\nclass Build: CliktCommand() {\nval file by argument().file()\noverride fun run() {\necho(\"Building $file\")\n}\n}\n\nfun main(args: Array<String>) = Compiler().subcommands(Clean(), Build()).main(args)\n
$ ./compiler clean --force build main.kt\nRunning compiler\nCleaning (force=true)\nBuilding main.kt\n
The parent command will run
once, and each subcommand will run
once each time they\u2019re called.
"},{"location":"commands/#parsing-multiple-subcommands","title":"Parsing multiple subcommands","text":"Note that enabling allowMultipleSubcommands
will disable allowInterspersedArgs
on the command and all its subcommands. If both were allowed to be enabled at the same time, then not all command lines could be parsed unambiguously.
When parsing in this mode, tokens are consumed greedily by a subcommand until it encounters an argument token it doesn\u2019t support, at which point the parent command resumes parsing where the subcommand left off. This means that if you have a subcommand with an argument().multiple()
parameter, you won\u2019t be able to call any other subcommands after that one, since it will consume the rest of the command line.
Subcommands of a command with allowMultipleSubcommands=true
can themselves have subcommands, but cannot have allowMultipleSubcommands=true
.
"},{"location":"documenting/","title":"Documenting Scripts","text":"Clikt takes care of creating formatted help messages for commands. There are a number of ways to customize the default behavior. You can also implement your own HelpFormatter
and set it on the command\u2019s context.
"},{"location":"documenting/#help-texts","title":"Help Texts","text":"Commands and parameters accept a help
argument. Commands also accept an epilog
argument, which is printed after the parameters and commands on the help page. All text is automatically trimmed of leading indentation and re-wrapped to the terminal width.
As an alternative to passing your help strings as function arguments, you can also use the help()
extensions for your options, and override commandHelp
and commandHelpEpilog
on your commands. These methods can access the terminal theme on the context to add color to your help text.
ExampleAlternate styleHelp output class Hello : CliktCommand(help = \"\"\"\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n \"\"\"\n) {\nval count by option(\"-c\", \"--count\", metavar=\"count\", help = \"number of greetings\").int().default(1)\nval name by argument()\noverride fun run() = repeat(count) { echo(\"Hello $name!\") }\n}\n
class Hello : CliktCommand() {\noverride fun commandHelp(context: Context): String {\nval style = context.theme.info\nreturn \"\"\"\n This script prints ${style(\"<name>\")} ${style(\"<count>\")} times.\n\n${style(\"<count>\")} must be a positive number, and defaults to 1.\n \"\"\".trimIndent()\n}\n\nval count by option(\"-c\", \"--count\", metavar=\"count\").int().default(1)\n.help { theme.success(\"number of greetings\") }\nval name by argument()\noverride fun run() = repeat(count) { echo(\"Hello $name!\") }\n}\n
$ ./hello --help\nUsage: hello [<options>] <name>\n\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n\nOptions:\n -c, --count <count> number of greetings\n -h, --help Show this message and exit\n
Option names and metavars will appear in help output even if no help string is specified for them. On the other hand, arguments only appear in the usage string. It is possible to add a help string to arguments which will be added to the help page, but the Unix convention is to just describe arguments in the command help.
"},{"location":"documenting/#markdown-in-help-texts","title":"Markdown in help texts","text":"All help texts use Mordant to render Markdown. You can use all the normal markdown features, such as lists, tables, and even hyperlinks if your terminal supports them.
ExampleHelp output class Tool : NoOpCliktCommand() {\nval option by option().help {\n\"\"\"\n | This | is | a | table |\n | ---- | -- | - | ----- |\n | 1 | 2 | 3 | 4 |\n\n - This is\n - a list\n\n ```\n You can\n use code blocks\n ```\n \"\"\".trimIndent()\n}\n}\n
Usage: tool [<options>]\n\nOptions:\n --option=<text> \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 This \u2502 is \u2502 a \u2502 table \u2502\n \u255e\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561\n \u2502 1 \u2502 2 \u2502 3 \u2502 4 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n \u2022 This is\n \u2022 a list\n\n \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n \u2502You can \u2502\n \u2502 use code blocks\u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n -h, --help Show this message and exit\n
"},{"location":"documenting/#manual-line-breaks","title":"Manual Line Breaks","text":"If you want to insert a line break manually without preformatting the entire paragraph, you can use the Unicode Next Line (NEL) character. You can type a NEL with the unicode literal \\u0085
.
Clikt will treat NEL similarly to how <br>
behaves in HTML: The NEL will be replaced with a line break in the output, and the paragraph will still be wrapped to the terminal width.
ExampleHelp output class Tool : NoOpCliktCommand() {\nval option by option()\n.help(\"This help will be at least two lines.\\u0085(this will start a new line)\")\n}\n
Usage: tool\n\nOptions:\n --option This help will be at least\n two lines.\n (this will start a new\n line)\n -h, --help Show this message and exit\n
Tip
In raw multiline strings (which do not parse escape sequences), you\u2019ll need to insert the NEL with a string template such as ${\"\\u0085\"}
.
"},{"location":"documenting/#subcommand-short-help","title":"Subcommand Short Help","text":"Subcommands are listed in the help page based on their name. They have a short help string which is the first line of their help.
ExampleUsage class Tool : NoOpCliktCommand()\n\nclass Execute : NoOpCliktCommand(help = \"\"\"\n Execute the command.\n\n The command will be executed.\n \"\"\")\n\nclass Abort : NoOpCliktCommand(help=\"Kill any running commands.\")\n
$ ./tool --help\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n execute Execute the command.\n abort Kill any running commands.\n
"},{"location":"documenting/#help-option-customization","title":"Help Option Customization","text":"Clikt handles the help option is specially. It is added automatically to every command. Any help option name that conflicts with another option is not used for the help option. If the help option has no unique names, it is not added.
You can change the help option\u2019s name and help message on the command\u2019s context:
ExampleUsage class HelpLocalization: Localization {\noverride fun helpOptionMessage(): String = \"show the help\"\n}\n\nclass Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpOptionNames = setOf(\"/help\")\nlocalization = HelpLocalization()\n}\n}\n}\n
$ ./tool /help\nUsage: tool [<options>]\n\nOptions:\n /help show the help\n
If you don\u2019t want a help option to be added, you can set helpOptionNames = emptySet()
"},{"location":"documenting/#default-values-in-help","title":"Default Values in Help","text":"You can configure the help formatter to show default values in the help output by passing showDefaultValues = true
to the MordantHelpFormatter
. By default, the string value of the default value will be shown. You can show a different value by passing the value you want to show to the defaultForHelp
parameter of default
.
ExampleUsage class Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, showDefaultValues = true) }\n}\n}\n\nval a by option(help = \"this is optional\").default(\"value\")\nval b by option(help = \"this is also optional\").default(\"value\", defaultForHelp=\"chosen for you\")\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --a <text> this is optional (default: value)\n --b <text> this is also optional (default: chosen for you)\n
"},{"location":"documenting/#required-options-in-help","title":"Required Options in Help","text":"By default, required
options are displayed the same way as other options. The help formatter includes two different ways to show that an option is required.
"},{"location":"documenting/#required-option-marker","title":"Required Option Marker","text":"You can pass a character to the requiredOptionMarker
argument of the MordantHelpFormatter
.
ExampleUsage class Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, requiredOptionMarker = \"*\") }\n}\n}\n\nval option by option(help = \"this is optional\")\nval required by option(help = \"this is required\").required()\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --option <text> this is optional\n* --required <text> this is required\n -h, --help Show this message and exit\n
"},{"location":"documenting/#required-option-tag","title":"Required Option Tag","text":"You can also show a tag for required options by passing showRequiredTag = true
to the MordantHelpFormatter
.
ExampleUsage class Tool : CliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, showRequiredTag = true) }\n}\n}\n\nval option by option(help = \"this is optional\")\nval required by option(help = \"this is required\").required()\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --option <text> this is optional\n --required <text> this is required (required)\n -h, --help Show this message and exit\n
"},{"location":"documenting/#grouping-options-in-help","title":"Grouping Options in Help","text":"You can group options into separate help sections by using OptionGroup and importing groups.provideDelegate. The name of the group will be shown in the output. You can also add an extra help message to be shown with the group. Groups can\u2019t be nested.
ExampleUsage import com.github.ajalt.clikt.parameters.groups.provideDelegate\n\nclass UserOptions : OptionGroup(\nname = \"User Options\",\nhelp = \"Options controlling the user\"\n) {\nval name by option(help = \"user name\")\nval age by option(help = \"user age\").int()\n}\n\nclass Tool : NoOpCliktCommand() {\nval userOptions by UserOptions()\n}\n
$ ./tool --help\nUsage: cli [<options>]\n\nUser Options:\n\n Options controlling the user\n\n --name <text> user name\n --age <int> user age\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"documenting/#suggesting-corrections-for-mistyped-parameters","title":"Suggesting Corrections for Mistyped Parameters","text":"When an option or subcommand is mistyped, Clikt will suggest corrections that are similar to the typed value.
Mistyped OptionMistyped Subcommand $ ./cli --sise=5\nError: no such option: \"--sise\". Did you mean \"--size\"?\n
$ ./cli building\nUsage: cli [<options>] <command> [<args>]...\n\nError: no such subcommand: \"building\". Did you mean \"build\"?\n
By default, Clikt will suggest corrections of any similar option or subcommand name based on a similarity metric. You can customize the suggestions by setting a correctionSuggestor
on your command\u2019s context.
class Cli : NoOpCliktCommand() {\ninit {\ncontext {\n// Only suggest corrections that start with the entered value\ncorrectionSuggestor = { enteredValue, possibleValues ->\npossibleValues.filter { it.startsWith(enteredValue) }\n}\n}\n}\n}\n
"},{"location":"documenting/#localization","title":"Localization","text":"You can localize error messages by implementing Localization
and setting the localization
property on your context.
ExampleUsage class CursiveLocalization : Localization {\noverride fun usageTitle() = \"\ud835\udcb0\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52:\"\noverride fun optionsTitle() = \"\ud835\udcaa\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8\"\noverride fun optionsMetavar() = \"\ud835\udc5c\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8\"\noverride fun helpOptionMessage() = \"\ud835\udcae\ud835\udcbd\ud835\udc5c\ud835\udccc \ud835\udcc9\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc2\ud835\udc52\ud835\udcc8\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52 \ud835\udcb6\ud835\udcc3\ud835\udcb9 \ud835\udc52\ud835\udccd\ud835\udcbe\ud835\udcc9\"\n\n// ... override the rest of the strings here\n}\n\nclass I18NTool : NoOpCliktCommand(help = \"\ud835\udcaf\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc9\ud835\udc5c\ud835\udc5c\ud835\udcc1 \ud835\udcbe\ud835\udcc8 \ud835\udcbe\ud835\udcc3 \ud835\udcb8\ud835\udcca\ud835\udcc7\ud835\udcc8\ud835\udcbe\ud835\udccb\ud835\udc52\") {\ninit {\ncontext { localization = CursiveLocalization() }\n}\n}\n
$ ./i18ntool --help\n\ud835\udcb0\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52: i18ntool [<\ud835\udc5c\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8>]\n\n \ud835\udcaf\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc9\ud835\udc5c\ud835\udc5c\ud835\udcc1 \ud835\udcbe\ud835\udcc8 \ud835\udcbe\ud835\udcc3 \ud835\udcb8\ud835\udcca\ud835\udcc7\ud835\udcc8\ud835\udcbe\ud835\udccb\ud835\udc52\n\n\ud835\udcaa\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8:\n -h, --help \ud835\udcae\ud835\udcbd\ud835\udc5c\ud835\udccc \ud835\udcc9\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc2\ud835\udc52\ud835\udcc8\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52 \ud835\udcb6\ud835\udcc3\ud835\udcb9 \ud835\udc52\ud835\udccd\ud835\udcbe\ud835\udcc9\n
"},{"location":"exceptions/","title":"Exception Handling","text":"Clikt uses exceptions internally to signal that processing has ended early for any reason. This includes incorrect command line usage, or printing a help page.
"},{"location":"exceptions/#where-are-exceptions-handled","title":"Where are Exceptions Handled?","text":"When you call CliktCommand.main
, it will parse the command line and catch any CliktError
exceptions. If it catches one, it will then print out the error\u2019s message and exit the process with the error\u2019s statusCode
. The message is printed to stderr or stdout depending on the error\u2019s printError
property.
For exceptions raised by Clikt, PrintMessage
and PrintHelpMessage
have a statusCode
of 0 and print to stdout. Other exceptions have a statusCode
of 1 and print to stderr.
Any other types of exceptions indicate a programming error, and are not caught by main
. However, convert
and the other parameter transformations will wrap exceptions thrown inside them in a UsageError
, so if you define a custom transformation, you don\u2019t have to worry about an exception escaping to the user.
"},{"location":"exceptions/#handling-exceptions-manually","title":"Handling Exceptions Manually","text":"CliktCommand.main
is just a try
/catch
block surrounding CliktCommand.parse
, so if you don\u2019t want exceptions to be caught, you can call parse
wherever you would normally call main
.
Tip
You can use echoFormattedHelp to print the help or error message to any exception, or getFormattedHelp to get the help message as a string.
fun main(args: Array<String>) {\nval cli = Cli()\ntry {\ncli.parse(args)\n} catch (e: CliktError) {\ncli.echoFormattedHelp(e)\nexitProcess(e.statusCode)\n}\n}\n
"},{"location":"exceptions/#which-exceptions-exist","title":"Which Exceptions Exist?","text":"All exceptions thrown by Clikt are subclasses of CliktError
.
The following subclasses exist:
Abort
: The command should exit immediately with the given statusCode
without printing any messages. PrintMessage
: The exception\u2019s message should be printed. PrintHelpMessage
: The help page for the exception\u2019s command should be printed. PrintCompletionMessage
: Shell completion code for the command should be printed. UsageError
: The command line was incorrect in some way. All the following exceptions subclass from this. These exceptions are automatically augmented with extra information about the current parameter, if possible. MultiUsageError
: Multiple UsageError
s occurred. The errors
property contains the list of the errors. ProgramResult
: The program should exit with the statusCode
from this exception. BadParameterValue
: A parameter was given the correct number of values, but of invalid format or type. MissingOption
and MissingArgument
: A required parameter was not provided. NoSuchOption
: An option was provided that does not exist. NoSuchSubcommand
: A subcommand was called that does not exist. IncorrectOptionValueCount
: An option was supplied but the number of values supplied to the option was incorrect. IncorrectArgumentValueCount
: An argument was supplied but the number of values supplied was incorrect. MutuallyExclusiveGroupException
: Multiple options in a mutually exclusive group were supplied when the group is restricted to a single value. FileNotFound
: A required configuration file or @-file was not found. InvalidFileFormat
: A configuration file or @-file failed to parse correctly.
"},{"location":"migration/","title":"Upgrading to Newer Releases","text":""},{"location":"migration/#upgrading-to-40","title":"Upgrading to 4.0","text":""},{"location":"migration/#help-formatting","title":"Help formatting","text":"The CliktHelpFormatter
class has been removed and replaced with the MordantHelpFormatter
. The MordantHelpFormatter
constructor takes a Context
instead of a Localization
, and the parameters controlling size and spacing have been removed. See the documentation for details on how to set the help formatter on the Context.
If you were subclassing CliktHelpFormatter
, MordantHelpFormatter
\u2018s open methods are different. See the helpformat
sample for an example of how to use the new formatter.
"},{"location":"migration/#prompting","title":"Prompting","text":"The CliktConsole
class has been removed. If you were using it, use your command\u2019s Mordant terminal
instead.
The prompt
and confirm
methods now use Mordant\u2019s prompting functionality, and some of their arguments have changed. In particular, conversion lambdas now return a ConversionResult
instead of throwing an exception.
In 4.0In 3.0 val input = prompt(\"Enter a number\") {\nit.toIntOrNull()\n?.let { ConversionResult.Valid(it) }\n?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\n
val input = prompt(\"Enter a number\") {\nit.toIntOrNull() ?: throw BadParameterValue(\"$it is not a valid integer\")\n}\n
"},{"location":"migration/#upgrading-to-30","title":"Upgrading to 3.0","text":""},{"location":"migration/#maven-coordinates","title":"Maven Coordinates","text":"Clikt\u2019s Maven groupId changed from com.github.ajalt
to com.github.ajalt.clikt
. So the full coordinate is now com.github.ajalt.clikt:clikt:3.0.0
.
With the new Multiplatform plugin in Kotlin 1.4, there is no longer a separate clikt-multiplatform
artifact. You can use com.github.ajalt.clikt:clikt:3.0.0
for both JVM-only and Multiplatform projects.
"},{"location":"migration/#environment-variable-splitting","title":"Environment variable splitting","text":"There used to be an envvarSplit
parameter to option()
and its convert()
that would split values coming from an environment variable. This parameter is removed, and values from environment variables are no longer split automatically.
If you still want to split option values, you can do so explicitly with split()
.
"},{"location":"migration/#experimental-apis","title":"Experimental APIs","text":"The Value Source API and Completion Generation APIs no longer require opt-in. You can use these APIs without needing the ExperimentalValueSourceApi
or ExperimentalCompletionCandidates
annotations.
"},{"location":"migration/#localization","title":"Localization","text":"By default, all strings are defined in the Localization
object set on your context.
This means that string parameters like usageTitle
in the constructor for CliktHelpFormatter
have been removed in favor of functions like Localization.usageTitle()
.
Context.helpOptionMessage
has also been removed in favor of Localization.helpOptionMessage()
. See Help Option Customization for an example.
"},{"location":"options/","title":"Options","text":"Options are added to commands by defining a property delegate with the option
function.
"},{"location":"options/#basic-options","title":"Basic Options","text":"The default option takes one value of type String
. The property is nullable. If the option is not given on the command line, the property value will be null. If the option is given at least once, the property will return the value of the last occurrence of the option.
ExampleUsage class Hello: CliktCommand() {\nval name by option(help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello --name=Foo\nHello, Foo!\n
"},{"location":"options/#option-names","title":"Option Names","text":"If you don\u2019t specify names for an option, a lowercase hyphen-separated name is automatically inferred from the property. For example, val myOpt by option()
will create an option that can be called with --my-opt
.
You can also specify any number of names for an option manually:
class Hello: CliktCommand() {\nval name by option(\"-n\", \"--name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
Option names that are two characters long (like -n
) are treated as POSIX-style short options. You call them with a value like this:
Usage 1Usage 2 $ ./hello -nfoo\nHello, foo!\n
$ ./hello -n foo\nHello, foo!\n
All other option names are considered long options, and can be called like this:
Usage 1Usage 2 $ ./hello --name=foo\nHello, foo!\n
$ ./hello --name foo\nHello, foo!\n
"},{"location":"options/#customizing-options","title":"Customizing Options","text":"The option behavior and delegate type can be customized by calling extension functions on the option
call. For example, here are some different option declarations:
val a: String? by option()\nval b: Int? by option().int()\nval c: Pair<Int, Int>? by option().int().pair()\nval d: Pair<Int, Int> by option().int().pair().default(0 to 0)\nval e: Pair<Float, Float> by option().float().pair().default(0f to 0f)\n
There are three main types of behavior that can be customized independently:
- The type of each value in the option. The value type is
String
by default, but can be customized with built-in functions like int
or choice
, or manually with convert
. This is detailed in the parameters page. - The number of values that the option requires. Options take one value by default, but this can be changed with built-in functions like
pair
and triple
, or manually with transformValues
. - How to handle all calls to the option (i.e. if the option is not present, or is present more than once). By default, the option delegate value is the null if the option is not given on the command line, and will use the value of the last occurrence if the option is given more than once. You can change this behavior with functions like
default
and multiple
.
Since the three types of customizations are orthogonal, you can choose which ones you want to use, and if you implement a new customization, it can be used with all the existing functions without any repeated code.
"},{"location":"options/#default-values","title":"Default Values","text":"By default, option delegates return null
if the option wasn\u2019t provided on the command line. You can instead return a default value with default
.
ExampleUsage 1Usage 2 class Pow : CliktCommand() {\nval exp by option(\"-e\", \"--exp\").double().default(1.0)\noverride fun run() {\necho(\"2 ^ $exp = ${(2.0).pow(exp)}\")\n}\n}\n
$ ./pow -e 8\n2 ^ 8.0 = 256.0\n
$ ./pow\n2 ^ 1.0 = 2.0\n
If the default value is expensive to compute, or you want to use another parameter as the default, you can use defaultLazy
instead of default
. It has the same effect, but you give it a lambda returning the default value, and the lambda will only be called if the option isn\u2019t present on the command line.
"},{"location":"options/#multi-value-options","title":"Multi Value Options","text":"Options can take a fixed number of values separated by whitespace, a variable number of values separated by a delimiter, or a variable number of values separated by a whitespace.
"},{"location":"options/#options-with-fixed-number-of-values","title":"Options With Fixed Number of Values","text":"There are built in functions for options that take two values (pair
, which uses a Pair
), or three values (triple
, which uses a Triple
). You can change the type of each value as normal with functions like int
.
If you need more values, you can provide your own container with transformValues
. You give that function the number of values you want, and a lambda that will transform a list of values into the output container. The list will always have a size equal to the number you specify. If the user provides a different number of values, Clikt will inform the user and your lambda won\u2019t be called.
ExampleUsage data class Quad<out T>(val a: T, val b: T, val c: T, val d: T)\nfun <T> Quad<T>.toList(): List<T> = listOf(a, b, c, d)\n\nclass Geometry : CliktCommand() {\nval square by option().int().pair()\nval cube by option().int().triple()\nval tesseract by option().int().transformValues(4) { Quad(it[0], it[1], it[2], it[3]) }\noverride fun run() {\necho(\"Square has dimensions ${square?.toList()?.joinToString(\"x\")}\")\necho(\"Cube has dimensions ${cube?.toList()?.joinToString(\"x\")}\")\necho(\"Tesseract has dimensions ${tesseract?.toList()?.joinToString(\"x\")}\")\n}\n}\n
$ ./geometry --square 1 2 --cube 3 4 5 --tesseract 6 7 8 9\nSquare has dimensions 1x2\nCube has dimensions 3x4x5\nTesseract has dimensions 6x7x8x9\n
"},{"location":"options/#splitting-an-option-value-on-a-delimiter","title":"Splitting an Option Value on a Delimiter","text":"You can use split
to allow a variable number of values to a single option invocation by separating the values with non-whitespace delimiters. This will also split values from environment variables.
ExampleUsageUsage with Environment Variable class C : CliktCommand() {\nval profiles by option(\"-P\", envvar=\"PROFILES\").split(\",\")\noverride fun run() {\nfor (profile in profiles) {\necho(profile)\n}\n}\n}\n
$ ./split -P profile-1,profile-2\nprofile-1\nprofile-2\n
$ export PROFILES=profile-1,profile-2\n$ ./split\nprofile-1\nprofile-2\n
"},{"location":"options/#options-with-an-optional-value","title":"Options with an Optional Value","text":"You can create options that take zero or one values with optionalValue
or optionalValueLazy
.
ExampleUsage with no option valueUsage with option valueUsage with no option class C : CliktCommand() {\nval log by option().optionalValue(\"debug\").default(\"none\")\noverride fun run() {\necho(\"log level: $log\")\n}\n}\n
$ ./command --log\nlog level: debug\n
$ ./command --log=verbose\nlog level: verbose\n
$ ./command\nlog level: none\n
Warning
If a user specifies the value without an =
, as in --log debug
, the debug
will always be interpreted as a value for the option, even if the command accepts arguments. This might be confusing to your users, so you can require that the option value always be specified with =
by passing optionalValue(acceptsUnattachedValue=false)
.
"},{"location":"options/#options-with-a-variable-number-of-values","title":"Options With a Variable Number of Values","text":"If you want your option to take a variable number of values, but want to split the value on whitespace [rather than a delimiter][#splitting-an-option-value-on-a-delimiter], you can use varargValues
.
ExampleUsage class Order : CliktCommand() {\nval sizes: List<String> by option().varargValues()\noverride fun run() {\necho(\"You ordered: $sizes\")\n}\n}\n
$ ./order --sizes small medium\nYou ordered: [\"small\", \"medium\"]\n
By default, varargValues
requires at least one value, and has no maximum limit. You can configure the limits by passing min
and max
arguments to varargValues
.
"},{"location":"options/#multiple-options","title":"Multiple Options","text":"Normally, when an option is provided on the command line more than once, only the values from the last occurrence are used. But sometimes you want to keep all values provided. For example, git commit -m foo -m bar
would create a commit message with two lines: foo
and bar
. To get this behavior with Clikt, you can use multiple
. This will cause the property delegate value to be a list, where each item in the list is the value of from one occurrence of the option. If the option is never given, the list will be empty (or you can specify a default to use).
ExampleUsage class Commit : CliktCommand() {\nval message: List<String> by option(\"-m\").multiple()\noverride fun run() {\necho(message.joinToString(\"\\n\"))\n}\n}\n
$ ./commit -m foo -m bar\nfoo\nbar\n
You can combine multiple
with item type conversions and multiple values.
val opt: List<Pair<Int, Int>> by option().int().pair().multiple()\n
"},{"location":"options/#default-values-for-optionmultiple","title":"Default values for option().multiple()","text":"You can also supply a default value to multiple
or require at least one value be present on the command line. These are specified as arguments rather than with separate extension functions since they don\u2019t change the type of the delegate.
RequiredDefault val opt: List<String> by option().multiple(required=true)\n
val opt: List<String> by option().multiple(default=listOf(\"default message\"))\n
"},{"location":"options/#deduplicating-optionmultiple-into-a-unique-set","title":"Deduplicating option().multiple() into a unique set","text":"You can discard duplicate values from a multiple
option with unique
.
ExampleUsage class Build : CliktCommand() {\nval platforms: Set<String> by option(\"-p\").multiple().unique()\noverride fun run() {\necho(\"Building for platforms: $platforms\")\n}\n}\n
$ ./build -p android -p ios -p android\nBuilding for platforms: [android, ios]\n
"},{"location":"options/#key-value-and-map-options","title":"Key-Value and Map Options","text":"You can split an option\u2019s value into a key-value pair with splitPair
. By default, the delimiter =
will be used to split. You can also use associate
to allow the option to be specified multiple times, and have its values collected in a map.
ExampleUsage class Build : CliktCommand() {\nval systemProp: Map<String, String> by option(\"-D\", \"--system-prop\").associate()\n\noverride fun run() {\necho(systemProp)\n}\n}\n
$ ./build -Dplace=here --system-prop size=small\n{place=here, size=small}\n
"},{"location":"options/#boolean-flag-options","title":"Boolean Flag Options","text":"Flags are options that don\u2019t take a value. Boolean flags can be enabled or disabled, depending on the name used to invoke the option. You can turn an option into a boolean flag with flag
. That function takes an optional list of secondary names that will be added to any existing or inferred names for the option. If the option is invoked with one of the secondary names, the delegate will return false. It\u2019s a good idea to always set secondary names so that a user can disable the flag if it was enabled previously.
ExampleUsage 1Usage 2 class Cli : CliktCommand() {\nval flag by option(\"--on\", \"-o\").flag(\"--off\", \"-O\", default = false)\noverride fun run() {\necho(flag)\n}\n}\n
$ ./cli -o\ntrue\n
$ ./cli --on --off\nfalse\n
Multiple short flag options can be combined when called on the command line:
ExampleUsage class Cli : CliktCommand() {\nval flagA by option(\"-a\").flag()\nval flagB by option(\"-b\").flag()\nval foo by option(\"-f\")\noverride fun run() {\necho(\"$flagA $flagB $foo\")\n}\n}\n
$ ./cli -abfFoo\ntrue true Foo\n
Tip
You can diasable short option grouping by setting Context.allowGroupedShortOptions
to false
.
"},{"location":"options/#counted-flag-options","title":"Counted Flag Options","text":"You might want a flag option that counts the number of times it occurs on the command line. You can use counted
for this.
ExampleUsage class Log : CliktCommand() {\nval verbosity by option(\"-v\").counted()\noverride fun run() {\necho(\"Verbosity level: $verbosity\")\n}\n}\n
$ ./log -vvv\nVerbosity level: 3\n
"},{"location":"options/#feature-switch-flags","title":"Feature Switch Flags","text":"Another way to use flags is to assign a value to each option name. You can do this with switch
, which takes a map of option names to values. Note that the names in the map replace any previously specified or inferred names.
ExampleUsage class Size : CliktCommand() {\nval size by option().switch(\n\"--large\" to \"large\",\n\"--small\" to \"small\"\n).default(\"unknown\")\noverride fun run() {\necho(\"You picked size $size\")\n}\n}\n
$ ./size --small\nYou picked size small\n
"},{"location":"options/#choice-options","title":"Choice Options","text":"You can restrict the values that a regular option can take to a set of values using choice
. You can also map the input values to new types.
ExampleUsage 1Usage 2Usage 3 class Digest : CliktCommand() {\nval hash by option().choice(\"md5\", \"sha1\")\noverride fun run() {\necho(hash)\n}\n}\n
$ ./digest --hash=md5\nmd5\n
$ ./digest --hash=sha256\nUsage: digest [<options>]\n\nError: Invalid value for \"--hash\": invalid choice: sha256. (choose from md5, sha1)\n
$ ./digest --help\nUsage: digest [<options>]\n\nOptions:\n --hash [md5|sha1]\n -h, --help Show this message and exit\n
"},{"location":"options/#mutually-exclusive-option-groups","title":"Mutually Exclusive Option Groups","text":"If choice
or switch
options aren\u2019t flexible enough, you can use mutuallyExclusiveOptions
to group any nullable options into a mutually exclusive group. If more than one of the options in the group is given on the command line, the last value is used.
If you want different types for each option, you can wrap them in a sealed class.
ExampleUsage 1Usage 2Usage 3 sealed class Fruit {\ndata class Oranges(val size: String): Fruit()\ndata class Apples(val count: Int): Fruit()\n}\nclass Order : CliktCommand() {\nval fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\noption(\"--oranges\").convert { Oranges(it) },\noption(\"--apples\").int().convert { Apples(it) }\n)\n\noverride fun run() = echo(fruit)\n}\n
$ ./order --apples=10\nApples(count=10)\n
$ ./order --oranges=small\nOranges(size=small)\n
$ ./order --apples=10 --oranges=large\nOranges(size=large)\n
You can enforce that only one of the options is given with single
:
ExampleUsage val fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\noption(\"--apples\").convert { Apples(it.toInt()) },\noption(\"--oranges\").convert { Oranges(it) }\n).single()\n
$ ./order --apples=10 --oranges=small\nUsage: order [<options>]\n\nError: option --apples cannot be used with --oranges\n
Like regular options, you can make the entire group required
, or give it a default
value.
Like other option groups, you can specify a name
and help
text for the group if you want to set the group apart in the help output.
"},{"location":"options/#co-occurring-option-groups","title":"Co-Occurring Option Groups","text":"Sometimes you have a set of options that only make sense when specified together. To enforce this, you can make an option group cooccurring
.
Co-occurring groups must have at least one required
option, and may also have non-required options. The required
constraint is enforced if any of the options in the group are given on the command line. If none of the options are given, the value of the group is null.
ExampleUsage 1Usage 2Usage 3 class UserOptions : OptionGroup() {\nval name by option().required()\nval age by option().int()\n}\nclass Tool : CliktCommand() {\nval userOptions by UserOptions().cooccurring()\noverride fun run() {\nuserOptions?.let {\necho(it.name)\necho(it.age)\n} ?: echo(\"No user options\")\n}\n}\n
$ ./tool\nNo user options\n
$ ./tool --name=jane --age=30\njane\n30\n
$ ./tool --age=30\nUsage: tool [<options>]\n\nError: Missing option \"--name\".\n
Like other option groups, you can specify a name
and help
text for the group if you want to set the group apart in the help output.
"},{"location":"options/#choice-and-switch-options-with-groups","title":"Choice and Switch Options With Groups","text":"If you have different groups of options that only make sense when another option has a certain value, you can use groupChoice
and groupSwitch
.
groupChoice
options are similar to choice
options, but instead of mapping a value to a single new type, they map a value to a co-occurring OptionGroup
. Options for groups other than the selected one are ignored, and only the selected group\u2019s required
constraints are enforced. In the same way, groupSwitch
options are similar to switch
options.
ExampleUsage 1Usage 2Usage 3Usage 4 sealed class LoadConfig(name: String): OptionGroup(name)\nclass FromDisk : LoadConfig(\"Options for loading from disk\") {\nval path by option().file().required()\nval followSymlinks by option().flag()\n}\n\nclass FromNetwork: LoadConfig(\"Options for loading from network\") {\nval url by option().required()\nval username by option().prompt()\nval password by option().prompt(hideInput = true)\n}\n\nclass Tool : CliktCommand(help = \"An example of a custom help formatter that uses ansi colors\") {\nval load by option().groupChoice(\n\"disk\" to FromDisk(),\n\"network\" to FromNetwork()\n)\n\noverride fun run() {\nwhen(val it = load) {\nis FromDisk -> echo(\"Loading from disk: ${it.path}\")\nis FromNetwork -> echo(\"Loading from network: ${it.url}\")\nnull -> echo(\"Not loading\")\n}\n}\n}\n
$ ./tool --load=disk --path=./config --follow-symlinks\nLoading from disk: .\\config\n
$ ./tool --load=network --url=www.example.com --username=admin\nPassword: *******\nLoading from network: www.example.com\n
$ ./tool --load=disk\nUsage: cli [<options>]\n\nError: Missing option \"--path\".\n
$ ./tool --load=whoops\nUsage: cli [<options>]\n\nError: Invalid value for \"--load\": invalid choice: whoops. (choose from disk, network)\n
"},{"location":"options/#number-options-without-a-name","title":"Number Options Without a Name","text":"If you have an int or long option, you might want to allow it to be specified without need the option name. For example, git log -2
and git log -n 2
are equivalent. You can add an option like this by passing acceptsValueWithoutName=true
to int()
or long()
.
ExampleUsage 1Usage 2Help Output class Tool : CliktCommand() {\nval level by option(\"-l\", \"--level\", metavar = \"<number>\")\n.int(acceptsValueWithoutName = true)\n\noverride fun run() {\necho(\"Level: $level\")\n}\n}\n
$ ./tool -20\nLevel: 20\n
$ ./tool --level=3\nLevel: 3\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n-<number>, -l, --level <number>\n-h, --help Show this message and exit\n
Caution
You may only have one option with acceptsValueWithoutName=true
per command
"},{"location":"options/#prompting-for-input","title":"Prompting For Input","text":"In some cases, you might want to create an option that uses the value given on the command line if there is one, but prompt the user for input if one is not provided. Clikt can take care of this for you with the prompt
function.
ExampleUsage 1Usage 2 class Hello : CliktCommand() {\nval name by option().prompt()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
./hello --name=foo\nHello foo\n
./hello\nName: foo\nHello foo\n
The default prompt string is based on the option name, but prompt
takes a number of parameters to customize the output.
"},{"location":"options/#password-prompts","title":"Password Prompts","text":"You can also create an option that uses a hidden prompt and asks for confirmation. This combination of behavior is commonly used for passwords.
ExampleUsage class Login : CliktCommand() {\nval password by option().prompt(requireConfirmation = true, hideInput = true)\noverride fun run() {\necho(\"Your hidden password: $password\")\n}\n}\n
$ ./login\nPassword:\nYour hidden password: hunter2\n
"},{"location":"options/#eager-options","title":"Eager Options","text":"Sometimes you want an option to halt execution immediately and print a message. For example, the built-on --help
option, or the --version
option that many programs have.
The --help
option is added automatically to commands, and --version
can be added using versionOption
. Since the option doesn\u2019t have a value, you can\u2019t define it using a property delegate. Instead, call the function on a command directly, either in an init
block, or on a command instance.
These definitions are equivalent:
Version 1Version 2Usage class Cli : NoOpCliktCommand() {\ninit {\nversionOption(\"1.0\")\n}\n}\nfun main(args: Array<String>) = Cli().main(args)\n
class Cli : NoOpCliktCommand()\nfun main(args: Array<String>) = Cli().versionOption(\"1.0\").main(args)\n
$ ./cli --version\ncli version 1.0\n
"},{"location":"options/#custom-eager-options","title":"Custom Eager Options","text":"If you want to define your own option with a similar behavior, you can do so by calling eagerOption
. This function takes an action
that is called when the option is encountered on the command line. To print a message and halt execution normally from the callback, you can throw a PrintMessage
exception, and CliktCommand.main
will take care of printing the message. If you want to exit normally without printing a message, you can throw Abort(error=false)
instead.
You can define your own version option like this:
class Cli : NoOpCliktCommand() {\ninit {\neagerOption(\"--version\") {\nthrow PrintMessage(\"$commandName version 1.0\")\n}\n}\n}\n
"},{"location":"options/#custom-eager-options-with-values","title":"Custom Eager Options With Values","text":"If you need an eager option that takes a value, pass eager=true
to option()
.
val color by option(eager=true).flag(\"--no-color\", default=true)\n
Warning
Eager options cannot reference other options, since they are finalized first. They can be declared in regular OptionGroups
, but not in other types of groups like [switch groups][#choice-and-switch-options-with-groups].
"},{"location":"options/#deprecating-options","title":"Deprecating Options","text":"You can communicate to users that an option is deprecated with option().deprecated()
. By default, this function will add a tag to the option\u2019s help message, and print a warning to stderr if the option is used.
You can customize or omit the warning message and help tags, or change the warning into an error.
ExampleUsage 1Usage 2Usage 3Usage 4Help Output class Cli : CliktCommand() {\nval opt by option(help = \"option 1\").deprecated()\nval opt2 by option(help = \"option 2\").deprecated(\"WARNING: --opt2 is deprecated, use --new-opt instead\", tagName = null)\nval opt3 by option(help = \"option 3\").deprecated(tagName = \"pending deprecation\", tagValue = \"use --new-opt instead\")\nval opt4 by option(help = \"option 4\").deprecated(error = true)\n\noverride fun run() = echo(\"command run\")\n}\n
$ ./cli --opt=x\nWARNING: option --opt is deprecated\ncommand run\n
$ ./cli --opt2=x\nWARNING: --op2 is deprecated, use --new-opt instead\ncommand run\n
$ ./cli --opt3=x\nWARNING: option --opt3 is deprecated\ncommand run\n
$ ./cli --opt4=x\nERROR: option --opt4 is deprecated\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n --opt <text> option 1 (deprecated)\n --opt2 <text> option 2\n --opt3 <text> option 3 (pending deprecation: use --new-opt instead)\n --opt4 <text> option 4 (deprecated)\n
"},{"location":"options/#unknown-options","title":"Unknown Options","text":"You may want to collect unknown options for manual processing. You can do this by passing treatUnknownOptionsAsArgs = true
to your CliktCommand
constructor. This will cause Clikt to treat unknown options as positional arguments rather than reporting an error when one is encountered. You\u2019ll need to define an argument().multiple()
property to collect the options, otherwise an error will still be reported.
ExampleUsage class Wrapper : CliktCommand(treatUnknownOptionsAsArgs = true) {\ninit { context { allowInterspersedArgs = false } }\n\nval command by option().required()\nval arguments by argument().multiple()\n\noverride fun run() {\nval cmd = (listOf(command) + arguments).joinToString(\" \")\nval proc = Runtime.getRuntime().exec(cmd)\necho(proc.inputStream.bufferedReader().readText())\nproc.waitFor()\n}\n}\n
$ ./wrapper --command=git tag --help | head -n4\nGIT-TAG(1) Git Manual GIT-TAG(1)\n\nNAME\n git-tag - Create, list, delete or verify a tag object signed with GPG\n
Warning
Multiple short options in a single token (e.g. using -abc
to specify -a
, -b
, and -c
in a single token) will still report an error if it contains a mixture of known and unknown options. To avoid this, don\u2019t declare single-letter names for options in commands that use treatUnknownOptionsAsArgs
.
You\u2019ll often want to set allowInterspersedArgs = false
on your Context when using treatUnknownOptionsAsArgs
. You may also find that subcommands are a better fit than treatUnknownOptionsAsArgs
for your use case.
"},{"location":"options/#values-from-environment-variables","title":"Values From Environment Variables","text":"Clikt supports reading option values from environment variables if they aren\u2019t given on the command line. This feature is helpful when automating tools. For example, when using git commit
, you can set the author date with a command line parameter: git commit --date=10/21/2015
. But you can also set it with an environment variable: GIT_AUTHOR_DATE=10/21/2015 git commit
.
Clikt will read option values from environment variables as long as it has an envvar name for the option. There are two ways to set that name: you can set the name manually for an option, or you can enable automatic envvar name inference.
To set the envvar name manually, pass the name to option
:
ExampleUsage 1Usage 2 class Hello : CliktCommand() {\nval name by option(envvar = \"MY_NAME\")\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ export MY_NAME=Foo\n$ ./hello\nHello Foo\n
$ export MY_NAME=Foo\n$ ./hello --name=Bar\nHello Bar\n
You can enable automatic envvar name inference by setting the autoEnvvarPrefix
on a command\u2019s context
. This will cause all options without an explicit envvar name to be given an uppercase underscore-separated envvar name. Since the prefix is set on the context
, it is propagated to subcommands. If you have a subcommand called foo
with an option --bar
, and your prefix is MY_TOOL
, the option\u2019s envvar name will be MY_TOOL_FOO_BAR
.
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext { autoEnvvarPrefix = \"HELLO\" }\n}\nval name by option()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ export HELLO_NAME=Foo\n$ ./hello\nHello Foo\n
"},{"location":"options/#multiple-values-from-environment-variables","title":"Multiple Values from Environment Variables","text":"You might need to allow users to specify multiple values for an option in a single environment variable. You can do this by creating an option with split
.
"},{"location":"options/#flag-option-values-from-environment-variables","title":"Flag Option Values from Environment Variables","text":"For flag options, any of the following (case-insensitive) environment variable values will be interpreted as true
:
\"true\"
, \"t\"
, \"1\"
, \"yes\"
, \"y\"
, \"on\"
The following (case-insensitive) values wil be interpreted as false
:
\"false\"
, \"f\"
, \"0\"
, \"no\"
, \"n\"
, \"off\"
All other values are invalid.
"},{"location":"options/#overriding-system-environment-variables","title":"Overriding system environment variables","text":"You can set a custom function that will be used instead of the system environment variables with ContextBuilder.envvarReader.
@Test\nfun `test envvar`() {\nval envvars = mapOf(\"MY_TOOL_OPTION\" to \"value\")\nval tool = MyTool().context {\nenvvarReader = { envvars[it] }\n}\ntool.parse(emptyList())\nassertEquals(\"value\", tool.option)\n}\n
"},{"location":"options/#values-from-configuration-files","title":"Values from Configuration Files","text":"Clikt also supports reading option values from one or more configuration files (or other sources) when they aren\u2019t present on the command line. For example, when using git commit
, you can set the author email with a command line parameter: git commit --author='Clikt <clikt@example.com>
. But you can also set it in your git configuration file: user.email=clikt@example.com
.
Clikt allows you to specify one or more sources of option values that will be read from with the Context.valueSource
builder.
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext {\nvalueSource = PropertiesValueSource.from(\"myconfig.properties\")\n}\n}\nval name by option()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ echo \"name=Foo\" > myconfig.properties\n$ ./hello\nHello Foo\n
You can also pass multiple sources to Context.valueSources
, and each source will be searched for the value in order.
Clikt includes support for reading values from a map, and (on JVM) from Java Properties files. For these two sources, you can customize the keys used to look up options by passing the result of ValueSource.getKey
or ValueSource.envvarKey
to the source\u2019s getKey
constructor parameter.
You can add any other file type by implementing ValueSource. See the JSON sample for an implementation that uses kotlinx.serialization to load values from JSON files.
"},{"location":"options/#configuration-files-and-environment-variables","title":"Configuration Files and Environment Variables","text":"Every option can read values from both environment variables and configuration files. By default, Clikt will use the value from an environment variable before the value from a configuration file, but you can change this by setting Context.readEnvvarBeforeValueSource
to false
.
"},{"location":"options/#windows-and-java-style-option-prefixes","title":"Windows and Java-Style Option Prefixes","text":"When specifying option names manually, you can use any prefix (as long as it\u2019s entirely punctuation).
For example, you can make a Windows-style interface with slashes:
ExampleUsage class Hello: CliktCommand() {\nval name by option(\"/name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello /name Foo\nHello, Foo!\n
Or you can make a Java-style interface that uses single-dashes for long options:
ExampleUsage class Hello: CliktCommand() {\nval name by option(\"-name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello -name Foo\nHello, Foo!\n
Note that inferred names will always have a POSIX-style prefix like --name
. If you want to use a different prefix, you should specify all option names manually.
"},{"location":"options/#option-transformation-order","title":"Option Transformation Order","text":"Clikt has a large number of extension functions that can modify options. When applying multiple functions to the same option, there\u2019s only one valid order for the functions to be applied. For example, option().default(3).int()
will not compile, because default
must be applied after the value type conversion.
You can call convert
multiple times, but you can only apply one transform of each other type. So option().default(\"\").multiple()
is invalid, since default
and multiple
both transform the call list (if you need a custom default value for multiple
, you can pass it one as an argument).
Here\u2019s an integer option with one of each available transform in a valid order:
val opt: Pair<Int, Int> by option(\"-o\", \"--opt\")\n.int()\n.restrictTo(1..100)\n.pair()\n.default(1 to 2)\n.validate { require(it.second % 2 == 0) }\n
"},{"location":"parameters/","title":"Parameters","text":"Clikt supports two types of parameters: options and positional arguments. If you\u2019re following Unix conventions with your interface, you should use options for most parameters. Options are usually optional, and arguments are frequently required.
"},{"location":"parameters/#differences","title":"Differences","text":"Arguments have the advantage of being able to accept a variable number of values, while Options are limited to a fixed number of values. Other than that restriction, options have more capabilities than arguments.
Options can:
- Act as flags (options don\u2019t have to take values)
- Prompt for missing input
- Load values from environment variables
In general, arguments are usually used for values like file paths or URLs, or for required values, and options are used for everything else.
"},{"location":"parameters/#parameter-names","title":"Parameter Names","text":"Both options and arguments can infer their names (or the metavar in the case of arguments) from the name of the property. You can also specify the names manually. Options can have any number of names, where arguments only have a single metavar.
ExampleHelp Output class Cli : CliktCommand() {\nval inferredOpt by option()\nval inferred by argument()\nval explicitOpt by option(\"-e\", \"--explicit\")\nval explicitArg by argument(\"<explicit>\")\noverride fun run() = Unit\n}\n
Usage: cli [<options>] <inferred> <explicit>\n\nOptions:\n --inferred-opt <text>\n -e, --explicit <text>\n -h, --help Show this message and exit\n
"},{"location":"parameters/#parameter-types","title":"Parameter Types","text":"Both options and arguments can convert the String that the user inputs to other types.
Types work by transforming the return value of the property delegate. By default, parameters have a string type:
val opt: String? by option(help=\"an option\")\nval arg: String by argument(help=\"an argument\")\n
To convert the input to an integer, for example, use the int()
extension function:
val opt: Int? by option(help=\"an option\").int()\nval arg: Int by argument(help=\"an argument\").int()\n
"},{"location":"parameters/#built-in-types","title":"Built-In Types","text":"There are a number of built-in types that can be applied to options and arguments.
"},{"location":"parameters/#int-and-long","title":"Int and Long","text":" option().int()
and argument().int()
option().long()
and argument().long()
option().uint()
and argument().uint()
option().ulong()
and argument().ulong()
By default, any value that fits in the integer type is accepted. You can restrict the values to a range with restrictTo()
, which allows you to either clamp the input to the range, or fail with an error if the input is outside the range.
"},{"location":"parameters/#float-and-double","title":"Float and Double","text":" option().float()
and argument().float()
option().double()
and argument().double()
As with integers, you can restrict the input to a range with restrictTo()
.
"},{"location":"parameters/#boolean","title":"Boolean","text":" option().flag()
option().boolean()
and argument().boolean()
You will normally want to use flags for boolean options. Explicit boolean value conversion is also available if you need, for example, a tri-state Boolean?
parameter.
"},{"location":"parameters/#choice","title":"Choice","text":" option().choice()
and argument().choice()
You can restrict the values to a set of values, and optionally map the input to a new value. For example, to create an option that only accepts the value \u201ca\u201d or \u201cb\u201d:
val opt: String? by option().choice(\"a\", \"b\")\n
You can also convert the restricted set of values to a new type:
val color: Int? by argument().choice(\"red\" to 1, \"green\" to 2)\n
Choice parameters accept values that are case-sensitive by default. This can be configured by passing ignoreCase = true
.
"},{"location":"parameters/#enum","title":"Enum","text":" option().enum()
and argument().enum()
Like choice
, but uses the values of an enum type.
enum class Color { RED, GREEN }\nval color: Color? by option().enum<Color>()\n
Enum parameters accept case-insensitive values by default. This can be configured by passing ignoreCase = false
.
You can also pass a lambda to map the enum values to option names.
val color: Color? by option().enum<Color> { it.name.lowercase() }\n
"},{"location":"parameters/#file-paths","title":"File paths","text":" option().file()
and argument().file()
option().path()
and argument().path()
These conversion functions take extra parameters that allow you to require that values are file paths that have certain attributes, such as that they are directories, or they are writable files.
"},{"location":"parameters/#file-path-inputstream-and-outputstreams","title":"File path InputStream
and OutputStream
s","text":" option().inputStream()
and argument().inputStream()
option().outputStream()
and argument().outputStream()
Like file and path, these conversions take file path values, but expose them as open streams for reading or writing. They support the unix convention of passing -
to specify stdin or stdout rather than a file on the filesystem. You\u2019ll need to close the streams yourself. You can also use stdin or stdout as their default values.
If you need to check if one of these streams is pointing to a file rather than stdin or stdout, you can use isCliktParameterDefaultStdin
or isCliktParameterDefaultStdout
.
"},{"location":"parameters/#custom-types","title":"Custom Types","text":"You can convert parameter values to a custom type by using argument().convert()
and option().convert()
. These functions take a lambda that converts the input String
to any type. If the parameter takes multiple values, or an option appears multiple times in argv
, the conversion lambda is called once for each value.
Any errors that are thrown from the lambda are automatically caught and a usage message is printed to the user. If you need to trigger conversion failure, you can use fail(\"error message\")
instead of raising an exception.
For example, you can create an option of type BigDecimal
like this:
ExampleUsage 1Usage 2 class Cli: CliktCommand() {\nval opt by option().convert { it.toBigDecimal() }\noverride fun run() = echo(\"opt=$opt\")\n}\n
$ ./cli --opt=1.5\nopt=1.5\n
$ ./cli --opt=foo\nUsage: cli [<options>]\n\nError: Invalid value for \"--opt\": For input string: \"foo\"\n
"},{"location":"parameters/#metavars","title":"Metavars","text":"You can also pass option().convert()
a metavar that will be printed in the help page instead of the default of value
. We can modify the above example to use a metavar and an explicit error message:
ExampleUsage 1Usage 2 class Cli: CliktCommand() {\nval opt by option(help=\"a real number\").convert(\"float\") {\nit.toBigDecimalOrNull() ?: fail(\"A real number is required\")\n}\noverride fun run() = echo(\"opt=$opt\")\n}\n
$ ./cli --opt=foo\nUsage: cli [<options>]\n\nError: Invalid value for \"--opt\": A real number is required\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n --opt <float> a real number\n -h, --help Show this message and exit\n
"},{"location":"parameters/#chaining","title":"Chaining","text":"You can call convert
more than once on the same parameter. This allows you to reuse existing conversion functions. For example, you could automatically read the text of a file parameter.
ExampleUsage class FileReader: CliktCommand() {\nval file: String by argument()\n.file(mustExist=true, canBeDir=false)\n.convert { it.readText() }\noverride fun run() {\necho(\"Your file contents: $file\")\n}\n}\n
$ echo 'some text' > myfile.txt\n$ ./filereader ./myfile.txt\nYour file contents: some text\n
"},{"location":"parameters/#parameter-validation","title":"Parameter Validation","text":"After converting a value to a new type, you can perform additional validation on the converted value with check()
and validate()
(or the argument equivalents).
"},{"location":"parameters/#check","title":"check()
","text":"check()
is similar the stdlib function of the same name: it takes lambda that returns a boolean to indicate if the parameter value is valid or not, and reports an error if it returns false. The lambda is only called if the parameter value is non-null.
ExampleUsage 1Usage 2Usage 3 class Tool : CliktCommand() {\nval number by option(help = \"An even number\").int()\n.check(\"value must be even\") { it % 2 == 0 }\n\noverride fun run() {\necho(\"number=$number\")\n}\n}\n
$ ./tool --number=2\nnumber=2\n
$ ./tool\nnumber=null\n
$ ./tool --number=1\nUsage: tool [<options>]\n\nError: invalid value for --number: value must be even\n
"},{"location":"parameters/#validate","title":"validate()
","text":"For more complex validation, you can use validate()
. This function takes a lambda that returns nothing, but can call fail(\"error message\")
if the value is invalid. You can also call require()
, which will fail if the provided expression is false. Like check
, the lambda is only called if the value is non-null.
The lambdas you pass to validate
are called after the values for all options and arguments have been set, so (unlike in transforms) you can reference other parameters:
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\nval number by option().int().default(0)\nval biggerNumber by option().int().validate {\nrequire(it > number) {\n\"--bigger-number must be bigger than --number\"\n}\n}\n\noverride fun run() {\necho(\"number=$number, biggerNumber=$biggerNumber\")\n}\n}\n
$ ./tool --number=1\nnumber=1, biggerNumber=null\n
$ ./tool --number=1 --bigger-number=0\nUsage: tool [<options>]\n\nError: --bigger-number must be bigger than --number\n
"},{"location":"quickstart/","title":"Quick Start","text":"You can get the library using any maven-compatible build system. Installation instructions can be found in the README.
"},{"location":"quickstart/#basic-concepts","title":"Basic Concepts","text":"Clikt command line interfaces are created by using property delegates inside a CliktCommand
. The normal way to use Clikt is to forward argv
from your main
function to CliktCommand.main
.
The simplest command with no parameters would look like this:
class Hello: CliktCommand() {\noverride fun run() {\necho(\"Hello World!\")\n}\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
And what it looks like to use:
$ ./hello\nHello World!\n
A help page is generated automatically:
$ ./hello --help\nUsage: hello [<options>]\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"quickstart/#printing-to-stdout-and-stderr","title":"Printing to Stdout and Stderr","text":"Why does this example use echo
instead of println
? Although println
works, it can cause problems with multi-platform support. echo
uses Mordant to print, so it supports colors and detects the current terminal to make sure that colors work on the current system. You can also pass err=true
to echo
to print to stderr instead of stdout.
Additionally, if you use Clikt\u2019s testing utilities, output sent to echo
will be captured for testing, but output sent to println
will not.
"},{"location":"quickstart/#nesting-commands","title":"Nesting Commands","text":"Instances of any command can be attached to other commands, allowing arbitrary nesting of commands. For example, you could write a script to manage a database:
ExampleUsageHelp Output class Database(name=\"db\"): CliktCommand() {\noverride fun run() = Unit\n}\n\nclass Init: CliktCommand(help=\"Initialize the database\") {\noverride fun run() {\necho(\"Initialized the database.\")\n}\n}\n\nclass Drop: CliktCommand(help=\"Drop the database\") {\noverride fun run() {\necho(\"Dropped the database.\")\n}\n}\n\nfun main(args: Array<String>) = Database()\n.subcommands(Init(), Drop())\n.main(args)\n
$ ./db init\nInitialized the database.\n\n$ ./db drop\nDropped the database.\n
$ ./db --help\nUsage: database [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n init Initialize the database\n drop Drop the database\n
"},{"location":"quickstart/#adding-parameters","title":"Adding Parameters","text":"To add parameters, use the option
and argument
property delegates:
ExampleHelp Output class Hello : CliktCommand() {\nval count by option(help=\"Number of greetings\").int().default(1)\nval name by argument()\n\noverride fun run() {\nfor (i in 1..count) {\necho(\"Hello $name!\")\n}\n}\n}\n
$ ./hello --help\nUsage: hello [<options>] <name>\n\nOptions:\n --count <int> Number of greetings\n -h, --help Show this message and exit\n
"},{"location":"quickstart/#developing-command-line-applications-with-gradle","title":"Developing Command Line Applications With Gradle","text":"When you write a command line application, you probably want to be able to run it without invoking java -jar ...
every time. If you\u2019re using Gradle, the application plugin provides a gradle task that bundles your program jars and scripts to launch them. It makes it easy to build a zip or tarball that you can distribute to your users without them needing to perform any incantations like setting up a classpath. You can see this plugin in use the in Clikt samples.
The application plugin also creates tasks that will build then run your main function directly from within gradle. You can pass command line arguments through to your app with the --args
flag:
$ ./gradlew run --args=\"--count=3 Clikt\"\n
A drawback to using the run
gradle task is that it redirects stdout, so Clikt will not print colors or prompt for input. You can configure the Mordant terminal that Clikt uses to always print with color, but this will cause ANSI codes to be printed even if you redirect the app\u2019s output to a file.
MyCommand().context {\nterminal = Terminal(ansiLevel = AnsiLevel.TRUECOLOR, interactive = true)\n}.main(args)\n
Another approach is to use the installDist
task provided by the plugin. This builds all the distribution scripts in your build folder, which you can then execute normally. See Clikt\u2019s runsample script for an example of this approach.
"},{"location":"testing/","title":"Testing Clikt Commands","text":"Clikt includes the test
extension to help testing commands and their output.
TestCommand @Test\nfun testHello() {\nval command = Hello()\nval result = command.test(\"--name Foo\")\nassertEqual(result.stdout, \"Hello, Foo!\")\nassertEqual(result.exitCode, 0)\nassertEqual(command.name, \"Foo\")\n}\n
class Hello: CliktCommand() {\nval name by option()\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
Calling test
will run the command with the given arguments and return a result object that contains the captured outputs and result status code. You can check the captured output with the stdout
property of the result, errors output with stderr
, or both combined in with output
.
Caution
Output printed with Kotlin\u2019s print
and println
functions are not captured. Use echo
instead.
"},{"location":"testing/#testing-environment-variables","title":"Testing Environment Variables","text":"You can set environment variables for your command by passing in a map of envvars
.
TestCommand @Test\nfun testHello() {\nval command = Hello()\nval result = command.test(\"\", envvars=mapOf(\"HELLO_NAME\" to \"Foo\"))\nassertEqual(result.stdout, \"Hello, Foo!\")\n}\n
class Hello: CliktCommand() {\nval name by option(envvar=\"HELLO_NAME\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
To keep tests reproducible, only the envvar values you provide to test
are visible to the command. To include system envvars as well, pass includeSystemEnvvars=true
to test
.
"},{"location":"testing/#testing-prompt-options","title":"Testing Prompt Options","text":"If you use prompt
options, you can use the stdin
parameter of test
to pass a string containing all the lines of input. If you have multiple prompts, each input should be separated by \\n
.
TestCommand @Test\nfun testAdder() {\nval command = Adder()\nval result = command.test(\"\", stdin = \"2\\n3\")\nassertEqual(result.stdout, \"first: second: result: 2 + 3 = 5\")\n}\n
class Adder : TestCommand() {\nval first by option().prompt()\nval second by option().prompt()\n\noverride fun run_() {\necho(\"result: $first + $second = ${first + second}\")\n}\n}\n
"},{"location":"testing/#custom-testing","title":"Custom Testing","text":"If the test
helper doesn\u2019t cover all the use cases you need to test, you can run your command yourself.
In unit tests, you won\u2019t want to use CliktCommand.main
, since it calls exitProcess
when errors occur. Instead, you should instead use CliktCommand.parse
, which throws exceptions with error details rather than printing the details and exiting the process. See the documentation on exceptions for more information on the exceptions that can be thrown.
If your command uses environment variables, you can configure the context to return test values for them.
To capture output, override the command\u2019s console.
"},{"location":"utilities/","title":"Utilities","text":"Writing command line interfaces often involves more than just parsing the command line. Clikt also provides functions to perform actions commonly used in command line programs.
"},{"location":"utilities/#launching-editors","title":"Launching Editors","text":"If you need to ask users for multi-line input, or need to have the user edit a file, you can do so through editText
and editFile
. These functions open the program defined in the VISUAL
or EDITOR
environment variables, or a sensible default if neither are defined. The functions return the edited text if the user saved their changes.
Example fun getCommitMessage(): String? {\nval message = \"\"\"\n # Enter your message.\n # Lines starting with # are ignored\n \"\"\".trimIndent()\nreturn editText(message, requireSave = true)\n?.replace(Regex(\"#[^\\n]*\\n\"), \"\")\n}\n
"},{"location":"utilities/#input-prompts","title":"Input Prompts","text":"Options can prompt for values automatically, but you can also do so manually by using Mordant\u2019s prompt functionality directly. By default, it accepts any input string, but you can also pass in a conversion function. If the conversion returns a ConversionResult.Invalid
, the prompt will ask the user to enter a different value.
ExampleInteractive Session val input = terminal.prompt(\"Enter a number\") {\nit.toIntOrNull()\n?.let { ConversionResult.Valid(it) }\n?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\necho(\"Twice your number is ${input * 2}\")\n
Enter a number: foo\nError: foo is not a valid integer\nEnter a number: 11\nTwice your number is 22\n
"},{"location":"utilities/#confirmation-prompts","title":"Confirmation Prompts","text":"You can also ask the user for a yes or no response with Mordant\u2019s YesNoPrompt
:
if (YesNoPrompt(\"Continue?\", terminal).ask() == true) {\necho(\"Ok!\")\n}\n
"},{"location":"whyclikt/","title":"Why Clikt?","text":"There are existing Kotlin libraries for creating command line interfaces, and many Java libraries work in Kotlin as well. However, none of them had all the following features:
- Unrestricted composability of commands
- Fully static type safety for parameters
- Composable parameter customization that doesn\u2019t require registering converter objects.
- Full support for Unix command line conventions
- Capable of reading parameter values from environment variables out of the box
- Included support for common functionality (keyboard interactivity, line ending normalization, launching editors, etc.)
- Built-in support for multi-token command aliases
Clikt is focused on making writing robust, posix-compliant command line interfaces as easy as possible. A good CLI does more than just parse argv
. It allows users to specify values in environment variables, and in some cases prompts for additional input, or opens an editor. Clikt supports all of this out of the box.
Sometimes you need to make a CLI that doesn\u2019t follow Unix conventions. You might be writing for windows, or you want to use the Java style of long options with a single dash. Maybe you need to use a bunch of required options instead of arguments, or you want the help page formatted differently. \u201cBest practices\u201d might not be the best for you, so Clikt tries to make implementing uncommon use-cases as easy as possible.
"},{"location":"whyclikt/#why-not-a-kotlin-library-like-kotlin-argparser-or-kotlinxcli","title":"Why not a Kotlin library like kotlin-argparser or kotlinx.cli?","text":"Clikt isn\u2019t the only Kotlin CLI library. kotlin-argparser and kotlinx.cli both predate Clikt\u2019s creation.
Both, like Clikt, use property delegates to define parameters, but they\u2019re missing most of Clikt features and its extensible design.
kotlinx.cli was written by JetBrains and mostly copied kotlin-argparser\u2018s design (and, later, some of Clikt\u2019s).
kotlin-argparser works well for simple cases. It\u2019s missing a lot of features that Clikt has, but features could be added. Its real drawback is that it fundamentally does not support composition of commands or parameter values. The lack of subcommand support was already a non-starter, but there are other design decisions that make it unsuitable.
In the simple cases, the two libraries are similar. Here\u2019s an example from its README:
class MyArgs(parser: ArgParser) {\nval v: Boolean by parser.flagging(help=\"enable verbose mode\")\nval username: String? by parser.storing(help=\"name of the user\")\nval count: Int? by parser.storing(help=\"number of the widgets\") { toInt() }\nval source: List<String> by parser.positionalList(help=\"source filenames\")\nval destination: String by parser.positional(help=\"destination\")\n}\n\nfun main(args: Array<String>) = mainBody {\nArgParser(args).parseInto(::MyArgs).run {\nprintln(\"Hello, $username!\")\nprintln(\"Moving $count widgets from $source to $destination.\")\n}\n}\n
Here\u2019s the same thing with Clikt:
class Cli : CliktCommand() {\nval v: Boolean by option(help = \"enable verbose mode\").flag()\nval username: String? by option(help = \"name of the user\")\nval count: Int? by option(help = \"number of the widgets\").int()\nval source: List<String> by argument(help = \"source filenames\").multiple()\nval destination: String by argument(help = \"destination\")\noverride fun run() {\nprintln(\"Hello, $name!\")\nprintln(\"Moving $count widgets from $source to $destination.\")\n}\n}\n\nfun main(args: Array<String>) = Cli().main(args)\n
Both work fine, although you may find Clikt more consistent and a bit less verbose. The differences become more pronounced once you try to do anything that isn\u2019t built in to kotlin-argparser.
Maybe you need an option to take two values. Here\u2019s another example from the kotlin-argparser
README showing how to do that:
fun ArgParser.putting(vararg names: String, help: String) =\noption<MutableMap<String, String>>(*names,\nargNames = listOf(\"KEY\", \"VALUE\"),\nhelp = help) {\nvalue.orElse { mutableMapOf<String, String>() }.apply {\nput(arguments.first(), arguments.last()) }\n}\n\nfun ArgParser.putting(help: String) =\nArgParser.DelegateProvider { identifier ->\nputting(identifierToOptionName(identifier), help = help) }\n\nclass MyArgs(parser: ArgParser) {\nval v by parser.putting(help=\"this takes two values\")\n}\n
Clikt has that functionality built in as option().pair()
, but you could implement it yourself like this:
class Cli : CliktCommand() {\nval v by option(help=\"this takes two values\").transformValues(2) { it[0] to it[1] }\n}\n
The Clikt version is of course much simpler, but there are more fundamental issues with the kotlin-argparser
version that drove the creation of Clikt:
- Its inheritance-based design means that if you wanted to change the type of each value, you would have to copy all the code for each type. With Clikt, you could just do
option().int().transformValues(2) { it[0] to it[1] }
- Its inheritance-based design means that supporting types, multiple values, and multiple option occurrences would require a combinatorial number of copies of the above code. With Clikt, these are all orthogonal.
- You have to do all error checking yourself. The
argparser
example silently discards extra values, or copies the single value, rather than inform the user of the mistake. You could write more code to do so, but Clikt takes care of it for you. - Option name inference is not automatic, requiring you to wrap the delegate with yet another function.
- Each delegate function has a different name, with no indication of whether it\u2019s creating an option or positional argument. With Clikt, all options are created with
option()
, and all arguments with argument()
.
Some of these problems can be solved by writing more code, and some can\u2019t. On the other hand, Clikt attempts to have a consistent, intuitive, composable design that does the right thing without forcing you to think about edge cases.
"},{"location":"whyclikt/#why-not-a-java-library-like-jcommander-or-picocli","title":"Why not a Java library like JCommander or Picocli?","text":"There are a lot of command line libraries for Java. Most are verbose and not composable. Two popular Java libraries that are usable from Kotlin are JCommander and picocli.
These libraries use annotations to define parameters, and reflection to set fields. This is functional for simple types, but defining your own types requires you to register a type adapter with the library. This means that type errors are not caught until runtime, and many types of customization are not possible.
For example, in JCommander, options that take multiple values cannot be converted to other types. The JCommander docs explain:
\u2026 only List is allowed for parameters that define an arity. You will have to convert these values yourself if the parameters you need are of type Integer or other (this limitation is due to Java\u2019s erasure).
You also can\u2019t customize many aspects of parsing in JCommander. It can\u2019t infer parameter names. With JCommander, you can\u2019t have an option with multiple values and multiple occurrences at the same time. You can\u2019t have more than one argument, and it can only take one value or an unlimited number of values. You can\u2019t nest subcommands.
JCommander and piocli are great libraries if you\u2019re writing code in Java, but we can do much better with Kotlin.
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"Clikt (pronounced \u201cclicked\u201d) is a multiplatform Kotlin library that makes writing command line interfaces simple and intuitive. It\u2019s the \u201cCommand Line Interface for Kotlin\u201d.
It is designed to make the process of writing command line tools effortless while supporting a wide variety of use cases and allowing advanced customization when needed.
Clikt has:
- arbitrary nesting of commands
- composable, type safe parameter values
- generation of help output and shell autocomplete scripts
- multiplatform packages for JVM, Node.js, and native Linux, Windows and macOS
What does it look like? Here\u2019s a complete example of a simple Clikt program:
class Hello : CliktCommand() {\nval count: Int by option().int().default(1).help(\"Number of greetings\")\nval name: String by option().prompt(\"Your name\").help(\"The person to greet\")\n\noverride fun run() {\nrepeat(count) {\necho(\"Hello $name!\")\n}\n}\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
And here\u2019s what it looks like when run:
The help page is generated for you:
Errors are also taken care of:
"},{"location":"#installation","title":"Installation","text":"Clikt is distributed through Maven Central.
dependencies {\nimplementation(\"com.github.ajalt.clikt:clikt:4.2.2\")\n}\n
"},{"location":"#if-youre-using-maven-instead-of-gradle-use-artifactidclikt-jvmartifactid","title":"If you\u2019re using Maven instead of Gradle, use <artifactId>clikt-jvm</artifactId>
","text":""},{"location":"#multiplatform","title":"Multiplatform","text":"Clikt supports the following targets: jvm
, mingwX64
, linuxX64
, macosX64
, and js
(for both Node.js and Browsers). Artifacts for macosArm64 are also published, but not tested with CI. See the docs for more information about functionality supported on each target. You\u2019ll need to use Gradle 6 or newer.
"},{"location":"#snapshots","title":"Snapshots","text":"Snapshot builds are also available You'll need to add the Sonatype snapshots repository:
repositories {\nmaven {\nurl = uri(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n}\n}\n
"},{"location":"#api-reference","title":"API Reference","text":" - Commands and Exceptions
- Options
- Arguments
- Parameter Type Conversions
- Output Formatting
"},{"location":"advanced/","title":"Advanced Patterns","text":""},{"location":"advanced/#common-options-with-subcommands","title":"Common Options With Subcommands","text":"In some cases, you will have multiple subcommands that all share a common set of options. For example, you may have an option for a config file, or an output directory, or some API credentials. There are several ways to structure your commands to avoid repeating the option declarations in each subcommand.
"},{"location":"advanced/#defining-common-options-on-the-root-command","title":"Defining Common Options on the Root Command","text":"You can define your options on the root command and pass down the information via the context. With this design, you\u2019ll have to specify the common options before the subcommand name on the command line.
ExampleUsage 1Usage 2 class Config(val token: String, val hostname: String)\n\nclass MyApi : CliktCommand() {\nprivate val token by option(help=\"api token to use for requests\").default(\"...\")\nprivate val hostname by option(help=\"base url for requests\").default(\"example.com\")\n\noverride fun run() {\ncurrentContext.obj = Config(token, hostname)\n}\n}\n\nclass Store : CliktCommand() {\nprivate val file by option(help=\"file to store\").file(canBeDir = false)\nprivate val config by requireObject<Config>()\noverride fun run() {\nmyApiStoreFile(config.token, config.hostname, file)\n}\n}\n\nclass Fetch : CliktCommand() {\nprivate val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\nprivate val config by requireObject<Config>()\noverride fun run() {\nmyApiFetchFile(config.token, config.hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi --hostname=https://example.com store file.txt\n
$ ./myapi --hostname=https://example.com fetch --outdir=./out\n
"},{"location":"advanced/#defining-common-options-in-a-group","title":"Defining Common Options in a Group","text":"Instead of defining your common options on the root command, you can instead define them in an OptionGroup which you include in each subcommand. This allows you to specify all options after the subcommand name.
ExampleUsage 1Usage 2 class CommonOptions: OptionGroup(\"Standard Options:\") {\nval token by option(help=\"api token to use for requests\").default(\"...\")\nval hostname by option(help=\"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : CliktCommand() {\nprivate val commonOptions by CommonOptions()\nprivate val file by option(help=\"file to store\").file(canBeDir = false)\noverride fun run() {\nmyApiStoreFile(commonOptions.token, commonOptions.hostname, file)\n}\n}\n\nclass Fetch : CliktCommand() {\nprivate val commonOptions by CommonOptions()\nprivate val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\noverride fun run() {\nmyApiFetchFile(commonOptions.token, commonOptions.hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi store --hostname=https://example.com file.txt\n
$ ./myapi fetch --hostname=https://example.com --outdir=./out\n
"},{"location":"advanced/#defining-common-options-in-a-base-class","title":"Defining Common Options in a Base Class","text":"A third design to share options is to define the common options in a base class that all the subcommands inherit from.
ExampleUsage 1Usage 2 abstract class MyApiSubcommand : CliktCommand() {\nval token by option(help = \"api token to use for requests\").default(\"...\")\nval hostname by option(help = \"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : MyApiSubcommand() {\nprivate val file by option(help = \"file to store\").file(canBeDir = false)\noverride fun run() {\nmyApiStoreFile(token, hostname, file)\n}\n}\n\nclass Fetch : MyApiSubcommand() {\nprivate val outdir by option(help = \"directory to store file in\").file(canBeFile = false)\noverride fun run() {\nmyApiFetchFile(token, hostname, outdir)\n}\n}\n\nfun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)\n
$ ./myapi store --hostname=https://example.com file.txt\n
$ ./myapi fetch --hostname=https://example.com --outdir=./out\n
"},{"location":"advanced/#command-aliases","title":"Command Aliases","text":"Clikt allows commands to alias command names to sequences of tokens. This allows you to implement common patterns like allowing the user to invoke a command by typing a prefix of its name, or user-defined aliases like the way you can configure git to accept git ci
as an alias for git commit
.
To implement command aliases, override CliktCommand.aliases
in your command. This function is called once at the start of parsing, and returns a map of aliases to the tokens that they alias to.
To implement git-style aliases:
ExampleUsage 1Usage 2 class Repo : NoOpCliktCommand() {\n// You could load the aliases from a config file etc.\noverride fun aliases(): Map<String, List<String>> = mapOf(\n\"ci\" to listOf(\"commit\"),\n\"cm\" to listOf(\"commit\", \"-m\")\n)\n}\n\nclass Commit: CliktCommand() {\nval message by option(\"-m\").default(\"\")\noverride fun run() {\necho(\"Committing with message: $message\")\n}\n}\n\nfun main(args: Array<String>) = Repo().subcommands(Commit()).main(args)\n
$ ./repo ci -m 'my message'\nCommitting with message: my message\n
$ ./repo cm 'my message'\nCommitting with message: my message\n
Note
Aliases are not expanded recursively: none of the tokens that an alias expands to will be expanded again, even if they match another alias.
You also use this functionality to implement command prefixes:
ExampleUsage class Tool : NoOpCliktCommand() {\noverride fun aliases(): Map<String, List<String>> {\nval prefixCounts = mutableMapOf<String, Int>().withDefault { 0 }\nval prefixes = mutableMapOf<String, List<String>>()\nfor (name in registeredSubcommandNames()) {\nif (name.length < 3) continue\nfor (i in 1..name.lastIndex) {\nval prefix = name.substring(0..i)\nprefixCounts[prefix] = prefixCounts.getValue(prefix) + 1\nprefixes[prefix] = listOf(name)\n}\n}\nreturn prefixes.filterKeys { prefixCounts.getValue(it) == 1 }\n}\n}\n\nclass Foo: CliktCommand() {\noverride fun run() {\necho(\"Running Foo\")\n}\n}\n\nclass Bar: CliktCommand() {\noverride fun run() {\necho(\"Running Bar\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Foo(), Bar()).main(args)\n
$ ./tool ba\nRunning Bar\n
"},{"location":"advanced/#token-normalization","title":"Token Normalization","text":"To prevent ambiguities in parsing, aliases are only supported for command names. However, there\u2019s another way to modify user input that works on more types of tokens. You can set a tokenTransformer
on the command\u2019s context that will be called for each option and command name that is input. This can be used to implement case-insensitive parsing, for example:
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext { tokenTransformer = { it.lowercase() } }\n}\n\nval name by option()\noverride fun run() = echo(\"Hello $name!\")\n}\n
$ ./hello --NAME=Clikt\nHello Clikt!\n
"},{"location":"advanced/#replacing-stdin-and-stdout","title":"Replacing stdin and stdout","text":"By default, functions like CliktCommand.main
and option().prompt()
read from stdin and write to stdout. If you want to use Clikt in an environment where the standard streams aren\u2019t available, you can set your own implementation of a TerminalInterface
when customizing the command context.
object MyInterface : TerminalInterface {\noverride val info: TerminalInfo\nget() = TerminalInfo(/* ... */)\n\noverride fun completePrintRequest(request: PrintRequest) {\nif (request.stderr) MyOutputStream.writeError(request.text)\nelse MyOutputStream.write(request.text)\n}\n\noverride fun readLineOrNull(hideInput: Boolean): String? {\nreturn if (hideInput) MyInputStream.readPassword()\nelse MyInputStream.readLine()\n}\n}\n\nclass CustomCLI : NoOpCliktCommand() {\ninit { context { terminal = Terminal(terminalInterface = MyInterface ) } }\n}\n
Tip
If you want to log the output, you can use Mordant\u2019s TerminalRecorder
. That\u2019s how test is implemented!
"},{"location":"advanced/#command-line-argument-files-argfiles","title":"Command Line Argument Files (\u201c@argfiles\u201d)","text":"Similar to javac
, Clikt supports loading command line parameters from a file using the \u201c@argfile\u201d syntax. You can pass any file path to a command prefixed with @
, and the file will be expanded into the command line parameters. This can be useful on operating systems like Windows that have command line length limits.
If you create a file named cliargs
with content like this:
--number 1\n--name='jane doe' --age=30\n./file.txt\n
You can call your command with the contents of the file like this:
$ ./tool @cliargs\n
Which is equivalent to calling it like this:
$ ./tool --number 1 --name='jane doe' --age=30 ./file.txt\n
You can use any file path after the @
, and can specify multiple @argfiles:
$ ./tool @../config/args @C:\\\\Program\\ Files\\\\Tool\\\\argfile\n
If you have any options with names that start with @
, you can still use @argfiles
, but values on the command line that match an option will be parsed as that option, rather than an @argfile
, so you\u2019ll have to give your files a different name.
"},{"location":"advanced/#preventing-argfile-expansion","title":"Preventing @argfile expansion","text":"If you want to use a value starting with @
as an argument without expanding it, you have three options:
- Pass it after a
--
, which disables expansion for everything that occurs after it. - Escape it with
@@
. The first @
will be removed and the rest used as the argument value. For example, @@file
will parse as the string @file
- Disable @argfile expansion entirely by setting
Context.expandArgumentFiles = false
"},{"location":"advanced/#file-format","title":"File format","text":" - Normal shell quoting and escaping rules apply.
- Line breaks are treated as word separators, and can be used where you would normally use a space to separate parameters.
- Line breaks can occur within quotes, and will be included in the quoted value.
- @argfiles can contain other @argfile arguments, which will be expanded recursively.
- An unescaped
#
character outside of quotes is treated as a line comment: it and the rest of the line are skipped. You can pass a literal #
by escaping it with \\#
or quoting it with '#'
. - If a
\\
occurs at the end of a line, the next line is trimmed of leading whitespace and the two lines are concatenated.
"},{"location":"advanced/#custom-exit-status-codes","title":"Custom exit status codes","text":"Clikt will normally exit your program with a status code of 0 for a normal execution, or 1 if there\u2019s an error. If you want to use a different value, you can throw ProgramResult(statusCode)
. If you use CliktCommand.main
, that exception will be caught and exitProcess
will be called with the value of statusCode
.
You could also call exitProcess
yourself, but the ProgramResult has a couple of advantages:
ProgramResult
is easier to test. Exiting the process makes unit tests difficult to run. ProgramResult
works on all platforms. exitProcess
is only available on the JVM.
"},{"location":"advanced/#multiplatform-support","title":"Multiplatform Support","text":"Clikt supports the following platforms in addition to JVM:
"},{"location":"advanced/#desktop-native-linux-windows-and-macos","title":"Desktop native (Linux, Windows, and macOS)","text":"All functionality is supported, except:
env
parameter of editText and editFile is ignored. - file and path parameter types are not supported.
"},{"location":"advanced/#nodejs","title":"NodeJS","text":"All functionality is supported, except:
- file and path parameter types are not supported.
"},{"location":"advanced/#browser-javascript","title":"Browser JavaScript","text":"All functionality is supported, except:
- The default terminal only outputs to the browser\u2019s developer console, which is probably not what you want. You can define your own TerminalInterface, or you can call parse instead of main and handle output yourself.
- editText and editFile are not supported.
- file and path parameter types are not supported.
"},{"location":"arguments/","title":"Arguments","text":"Arguments are declared and customized similarly to options, but are provided on the command line positionally instead of by name. Arguments are declared with argument()
, and the order that they are declared defines the order that they must be provided on the command line.
"},{"location":"arguments/#basic-arguments","title":"Basic Arguments","text":"By default, argument
takes a single String
value which is required to be provided on the command line.
ExampleUsage class Hello : CliktCommand() {\nval name by argument()\noverride fun run() {\necho(\"Hello $name!\")\n}\n}\n
$ ./hello Foo\nHello Foo!\n
Arguments appear in the usage string, but listed in the help page unless you set their help
value. It\u2019s usually clearer to document arguments in the command help.
ExampleHelp Output class Cp : CliktCommand(\nhelp = \"Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\"\n) {\nprivate val source by argument().file(mustExist = true).multiple()\nprivate val dest by argument().file()\noverride fun run() {\n// ...\n}\n}\n
Usage: cp [<options>] [<source>]... <dest>\n\n Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"arguments/#variadic-arguments","title":"Variadic Arguments","text":"Like options, arguments can take any fixed number of values, which you can change with functions like pair
and triple
. Unlike options, arguments can also take a variable (or unlimited) number of values. This is common with file path arguments, since they are frequently expanded with a glob pattern on the command line.
Variadic arguments are declared with multiple
. You can declare any number of arguments with fixed numbers of values, but only one variadic argument in a command.
ExampleUsage class Copy : CliktCommand() {\nval source: List<Path> by argument().path(mustExist = true).multiple()\nval dest: Path by argument().path(canBeFile = false)\noverride fun run() {\necho(\"Copying files $source to $dest\")\n}\n}\n
$ ./copy file.* out/\nCopying files [file.txt, file.md] to out/\n
You can also use unique
to discard duplicates:
val source: Set<Path> by argument().path(mustExist = true).multiple().unique()\n
"},{"location":"arguments/#option-like-arguments-using-","title":"Option-Like Arguments (Using --
)","text":"Clikt normally parses any value that starts with punctuation as an option, which allows users to intermix options and arguments. However, sometimes you need to pass a value that starts with punctuation to an argument. For example, you might have a file named -file.txt
that you want to use as an argument.
Clikt supports the POSIX convention of using --
to force all following values to be treated as arguments. Any values before the --
will be parsed normally.
ExampleUsage 1Usage 2 class Touch : CliktCommand() {\nval verbose by option().flag()\nval files by argument().multiple()\noverride fun run() {\nif (verbose) echo(files.joinToString(\"\\n\"))\n}\n}\n
$ ./touch --foo.txt\nUsage: touch [<options>] [<files>]...\n\nError: no such option: \"--foo.txt\".\n
$ ./touch --verbose -- --foo.txt bar.txt\n--foo.txt\nbar.txt\n
"},{"location":"autocomplete/","title":"Shell Autocomplete","text":"Clikt includes built-in support for generating autocomplete scripts for bash, zsh and fish shells.
Example $ ./repo <TAB><TAB>\ncommit clone pull\n\n$ ./repo -<TAB>\n--config -h --help --repo-home --verbose\n\n$./repo --repo-home ./g<TAB>\n./git ./got ./good\n
"},{"location":"autocomplete/#enabling-completion","title":"Enabling Completion","text":"Clikt handles autocomplete by generating a shell script that defines the completion. You generate the script once each time your CLI changes, and load it each time your start your shell.
"},{"location":"autocomplete/#with-an-environment-variable","title":"With an environment variable","text":"You can generate the completion script by invoking your program with a special environment variable.
You can set the variable name manually with the autoCompleteEnvvar
parameter in the CliktCommand
constructor. By default, it\u2019s your command\u2019s name capitalized, with -
replaced with _
, and prefixed with another _
. So if your command name is my-command
, the variable would be _MY_COMMAND_COMPLETE=bash
, _MY_COMMAND_COMPLETE=zsh
, or _MY_COMMAND_COMPLETE=fish
, depending on your current shell.
For example to activate bash autocomplete for this command:
class MyProgram: CliktCommand() {\n// ...\n}\n
You can generate the completion script and save it to a file like this:
$ _MY_PROGRAM_COMPLETE=bash ./my-program > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#with-an-option","title":"With an option","text":"If you\u2019d prefer not to use environment variables, you can add a special option to your command with the completionOption
function. Invoking your program with this option will generate the completion script:
Example 1Example 2Usage class MyCommand: CliktCommand() {\ninit {\ncompletionOption()\n}\n// ...\n}\n
class MyCommand: CliktCommand() {\n//..\n}\n\nfun main(args: Array<String>) = MyCommand().completionOption().main(args)\n
$ ./my-command --generate-completion=bash > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#with-a-subcommand","title":"With a subcommand","text":"A third option is to add a subcommand that will generate the completion when invoked.
Example 1Example 2Usage class MyCommand: CliktCommand() {\ninit {\nsubcommands(CompletionCommand())\n}\n// ...\n}\n
class MyCommand: CliktCommand() {\n//..\n}\n\nfun main(args: Array<String>) = MyCommand().subcommands(CompletionCommand()).main(args)\n
$ ./my-command generate-completion bash > ~/my-program-completion.sh\n
"},{"location":"autocomplete/#using-the-generated-script","title":"Using the generated script","text":"Once you\u2019ve generated the completion script, source the file to activate completion:
$ source ~/my-program-completion.sh\n
You can add that source command to your startup script so that completion is always available. For example, with bash:
$ echo source ~/my-program-completion.sh >> ~/.bashrc\n
You\u2019ll need to regenerate the completion script any time your command structure changes.
"},{"location":"autocomplete/#supported-functionality","title":"Supported Functionality","text":""},{"location":"autocomplete/#bash-and-zsh","title":"Bash and Zsh","text":"Currently subcommand, option, and command alias names can be completed, as well as values for options and arguments. choice
parameters are completed with their possible values. Other parameter types are completed as file or directory names. Context.allowInterspersedArgs
is supported.
"},{"location":"autocomplete/#fish","title":"Fish","text":"Fish\u2019s completion mechanism is more limited that Bash\u2019s. Subcommands can be completed, options can be completed as long as they start with a -
. Completion suggestions for positional arguments are the union of all positional arguments. Other advanced Clikt features are not supported.
"},{"location":"autocomplete/#customizing-completions","title":"Customizing Completions","text":"There is built-in completion for values for choice
parameters, and for parameters converted with file
and path
.
You can add completion for other parameters with the completionCandidates
parameter to option()
and argument()
. The value can be one of the following:
None
: The default. The parameter\u2019s values will not be completed. Path
: Completions will be filesystem paths. Hostname
: Completions will be read from the system\u2019s hosts file. Username
: Completions will be taken from the system\u2019s users. Fixed
: Completions are given as a fixed set of strings. Custom
: Completions are generated from a custom script.
"},{"location":"autocomplete/#custom-completion-candidates","title":"Custom
completion candidates","text":"The Custom
type takes a block that returns code to add to the script which generates completions for the given parameter.
If you just want to call another script or binary that prints all possible completion words to stdout, you can use fromStdout.
Both Bash and ZSH scripts use Bash\u2019s Programmable Completion system (ZSH via a comparability layer). The string returned from [generator] should be the body of a function that will be passed to compgen -F
.
Specifically, you should set the variable COMPREPLY
to the completion(s) for the current word being typed. The word being typed can be retrieved from the COMP_WORDS
array at index COMP_CWORD
.
Example with fromStdoutExample with full script class Hello: CliktCommand() {\n// This example uses `echo`, but you would use your own binary\n// or script that prints the completions.\nval name by option(completionCandidates =\nCompletionCandidates.Custom.fromStdout(\"echo completion1 completion2\")\n)\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
class Hello: CliktCommand() {\n// This is identical to the previous example\nval name by option(completionCandidates = CompletionCandidates.Custom {\n\"\"\"\n WORDS=${'$'}(echo completion1 completion2)\n COMPREPLY=(${'$'}(compgen -W \"${'$'}WORDS\" -- \"${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}\"))\n \"\"\".trimIndent()\n})\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
"},{"location":"autocomplete/#limitations","title":"Limitations","text":"Token Normalization is not supported.
If you have arguments that occur after a multiple
argument, those arguments won\u2019t be autocompleted. Partial command lines are ambiguous in those situations, and Clikt assumes that you\u2019re trying to complete the multiple
argument rather than the later ones.
Bash must be at least version 3, or Zsh must be at least version 4.1.
"},{"location":"changelog/","title":"Change Log","text":""},{"location":"changelog/#422","title":"4.2.2","text":""},{"location":"changelog/#changed","title":"Changed","text":" - Options and arguments can now reference option groups in their
defaultLazy
and other finalization blocks. They can also freely reference each other, including though chains of references. (#473) - Updated Kotlin to 1.9.21 (#472)
"},{"location":"changelog/#421","title":"4.2.1","text":""},{"location":"changelog/#added","title":"Added","text":" - Added
toString
implementations to options and arguments. (#434) - Added
CliktCommand.test
overload that takes a vararg of String
s as the command line arguments. Thanks to @sschuberth for the contribution (#451)
"},{"location":"changelog/#fixed","title":"Fixed","text":" - Update Mordant dependency to fix crashes on native targets and GraalVM (#447)
"},{"location":"changelog/#420","title":"4.2.0","text":""},{"location":"changelog/#added_1","title":"Added","text":" - Added
requireConfirmation
parameter to option().prompt()
(#426) - Added
CliktCommand.terminal
extension for accessing the terminal from a command. - Added
includeSystemEnvvars
, ansiLevel
, width
, and height
parameters to all CliktCommand.test
overloads.
"},{"location":"changelog/#deprecated","title":"Deprecated","text":" - Deprecated
CliktCommand.prompt
, use CliktCommand.terminal.prompt
or Prompt
instead. - Deprecated
CliktCommand.confirm
, use YesNoPrompt
instead.
"},{"location":"changelog/#fixed_1","title":"Fixed","text":" - Fixed incorrect error message when a
defaultLazy
option referenced a required
option. (#430)
"},{"location":"changelog/#410","title":"4.1.0","text":""},{"location":"changelog/#added_2","title":"Added","text":" - Added
MordantHelpFormatter.renderAttachedOptionValue
that you can override to change how option values are shown, e.g. if you want option to show as --option <value>
instead of --option=<value>
. (#416) - Added
option().optionalValueLazy{}
, which work like optionalValue()
but the default value is computed lazily. (#381)
"},{"location":"changelog/#changed_1","title":"Changed","text":" - Updated Kotlin to 1.9.0
PrintMessage
, PrintHelpMessage
and PrintCompletionMessage
now default to exiting with a status code 0, which is the behavior they had in 3.x. (#419)
"},{"location":"changelog/#400","title":"4.0.0","text":""},{"location":"changelog/#added_3","title":"Added","text":" - Added
Context.errorEncountered
which is true if parsing has continued after an error was encountered. option().help{\"\"}
and argument().help{\"\"}
extensions that set the parameter\u2019s help text lazily, with access to the current context so that you can add colors.
"},{"location":"changelog/#changed_2","title":"Changed","text":" Option.optionHelp
and Argument.argumentHelp
, CliktCommand.commandHelp
, and CliktCommand.commandHelpEpilog
are now methods that take the context as an argument, and the help
parameter to copy
is now a helpGetter
lambda. CliktCommand.shortHelp
now takes the context as an argument. - The
message
method on TransformContext
interfaces is now an extension.
"},{"location":"changelog/#deprecated_1","title":"Deprecated","text":" - Deprecated
CliktCommand.commandHelp
and commandHelpEpilog
properties in favor of the methods with the same name.
"},{"location":"changelog/#400-rc","title":"4.0.0-RC","text":""},{"location":"changelog/#added_4","title":"Added","text":" - You can now use markdown in your help strings, including tables and lists. Clikt uses the Mordant library for rendering.
- Help output and error messages now include colors by default. You can disable this or customize the styling by configuring the
context.terminal
- Added
Option.varargValues()
to create an option that accepts a variable number of values - Added
Option.optionalValue()
to create an option whose value is optional. - Added
obj
setter to context builder as an alternative to currentContext.obj
- Added
boolean()
parameter type conversions. - Added
uint()
and ulong()
parameter type conversions. - Added
nullableFlag()
parameter transformation. - Added
CliktCommand.test
extension for testing your commands and their output - Clikt will now report multiple errors if they occur via the new
MultiUsageError
exception, rather than just reporting the first error. (#367) - Added
CliktCommand.allHelpParams()
, which can be overridden to change which parameters are displayed in help output - Added
Context.argumentFileReader
which allows custom loading of argument files - Added
Context.allowGroupedShortOptions
which can disable parsing -abc
as -a -b -c
- Options named
-?
or /?
are now supported - Added
option(eager=true)
to create an eager option that takes values - Added
option(acceptsUnattachedValue=false)
to force the option to only accept values like --option=1
and not --option 1
- Added
CliktCommand.test()
that captures the output of a command and does not exit the process.
"},{"location":"changelog/#removed","title":"Removed","text":" - Removed
CliktConsole
. Mordant is now used for all input and output. If you were defining a custom console, instead define a mordant TerminalInterface
and set it on your context\u2019s Terminal
. - Removed
TermUi.echo
, TermUi.prompt
, and TermUi.confirm
. Use the equivalent methods on your CliktCommand
, or use mordant\u2019s prompts directly. - Removed legacy JS publications. Now only the JS/IR artifacts are published.
- Removed
CliktHelpFormatter
. Use MordantHelpFormatter
instead. - Removed
FlagOption
and EagerOption
classes. All options are now implemented as transformations on OptionWithValues
. FlagOption
is now OptionWithValues<Boolean, Boolean, Boolean>
.
"},{"location":"changelog/#changed_3","title":"Changed","text":" prompt
and confirm
are now implemented with mordant\u2019s prompt functionality, and the method parameters have changed to match mordant\u2019s - When using
treatUnknownOptionsAsArgs
, grouped short options like -abc
will be treated as an argument rather than reporting an error as long as they don\u2019t match any short options in the command. (#340) - Clikt no longer automatically calls
trimIndent
on strings passed to help
. Call trimIndent
or trimMargin
yourself if necessary. Context.Builder.helpOptionNames
now accepts any iterable rather than just a set. CliktCommand.echo
and prompt
are now public. (#407) - Internally, all options are implemented transformations on
OptionWithValues
, rather than using separate classes for each option type. - Some Localization strings have changed, removed
Localization.aborted()
, added Localization.argumentsMetavar()
Context.Builder.helpFormatter
is now a lambda that takes the current context as an argument - Exceptions have been reworked so that all exceptions thrown by Clikt are subclasses of
CliktError
. CliktError
now includes statusCode
and printError
properties. - The constructor of
UsageError
and its subclasses no longer takes a context
parameter. The context is now inferred automatically. UsageError.formatUsage
now takes the localization and formatter as arguments
"},{"location":"changelog/#fixed_2","title":"Fixed","text":" - When parsing a command line with more than one error, Clikt will now always report the error that occurs earliest if it can\u2019t report them all (#361)
- When
treatUnknownOptionsAsArgs
is true, grouped unknown short options will now be treated as arguments rather than reporting an error.
"},{"location":"changelog/#354","title":"3.5.4","text":""},{"location":"changelog/#fixed_3","title":"Fixed","text":" - Revert jvm jars to target Java 8
"},{"location":"changelog/#353","title":"3.5.3","text":""},{"location":"changelog/#changed_4","title":"Changed","text":" - Updated Kotlin to 1.8.22
"},{"location":"changelog/#fixed_4","title":"Fixed","text":" - Context is now set properly on NoSuchOption exceptions when thrown from subcommands. (#399)
- When
treatUnknownOptionsAsArgs
is true, grouped unknown short options will now be treated as arguments rather than reporting an error.
"},{"location":"changelog/#352","title":"3.5.2","text":""},{"location":"changelog/#changed_5","title":"Changed","text":" - Updated Kotlin to 1.8.10
"},{"location":"changelog/#fixed_5","title":"Fixed","text":" - Fix
CliktCommand.prompt
on NodeJS targets that would hang due to KT-55817 (#387)
"},{"location":"changelog/#351","title":"3.5.1","text":""},{"location":"changelog/#changed_6","title":"Changed","text":" - Updated Kotlin to 1.7.20
"},{"location":"changelog/#fixed_6","title":"Fixed","text":" - Support unicode in environment variable values on Native Windows. (#362)
- Support environment variables for options in a mutually exclusive options group. (#384)
"},{"location":"changelog/#350","title":"3.5.0","text":""},{"location":"changelog/#added_5","title":"Added","text":" - Added
hidden
parameter to CliktCommand
, which will prevent the command from being displayed as a subcommand in help output (#353) - Publish artifacts for the
macosArm64
target. Note that this target is not tested on CI. (#352)
"},{"location":"changelog/#changed_7","title":"Changed","text":" - Default values for arguments will now be included in help output when
showDefaultValues=true
is set on your help formatter (#357)
"},{"location":"changelog/#fixed_7","title":"Fixed","text":" - Fix flags and other options with defaults not being usable in
mutuallyExclusiveOptions
(#349) - Fix
CompletionCommand
generating completion for itself (#355)
"},{"location":"changelog/#342","title":"3.4.2","text":""},{"location":"changelog/#deprecated_2","title":"Deprecated","text":" TermUi.echo
, TermUi.prompt
, and TermUi.confirm
. Use the equivalent methods on CliktCommand
instead. (#344)
"},{"location":"changelog/#341","title":"3.4.1","text":""},{"location":"changelog/#added_6","title":"Added","text":" - Added
obj
setter to context builder as an alternative to currentContext.obj
- Added
option().boolean()
and argument().boolean()
uint()
and ulong()
parameter type conversions. CliktCommand.test
extension for testing your commands and their output
"},{"location":"changelog/#changed_8","title":"Changed","text":" - Updated Kotlin to 1.6.20
"},{"location":"changelog/#340","title":"3.4.0","text":""},{"location":"changelog/#changed_9","title":"Changed","text":" unique()
now works with any option with a list type, not just multiple()
options (#332) - Updated Kotlin to 1.6.10
"},{"location":"changelog/#fixed_8","title":"Fixed","text":" - Fixed co-occurring option groups returning null when all options in the group are defined in environment variables (#330)
"},{"location":"changelog/#330","title":"3.3.0","text":""},{"location":"changelog/#added_7","title":"Added","text":" - Added
default
parameter to argument().multiple()
(#305) Context.originalArgv
that allows you to read the command line arguments from within a command\u2019s run
(#290) context { envarReader = {...} }
to set a custom function to read from environment variables (#299)
"},{"location":"changelog/#changed_10","title":"Changed","text":" defaultLazy
values can now reference other parameters, as long the referenced parameters do not also reference other parameters - You can now call
CliktCommand.context
multiple times on the same command, and all builder blocks will be applied - Validate values entered to a
prompt
option, and show another prompt if the validation fails (#288) - Updated kotlin to 1.5.31
"},{"location":"changelog/#fixed_9","title":"Fixed","text":" - Report error when excess arguments are given to a command with
allowMultipleSubcommands=true
(#303)
"},{"location":"changelog/#320","title":"3.2.0","text":""},{"location":"changelog/#added_8","title":"Added","text":" InputStream.isCliktParameterDefaultStdin
and OutputStream.isCliktParameterDefaultStdout
to check if the streams returned from inputStream
/outputStream
options are proxying stdin/stdout (#272)
"},{"location":"changelog/#changed_11","title":"Changed","text":" - Make parameters of
mutuallyExclusiveOptions
covariant to allow validation without explicit type annotations. (#265) - Updated kotlin to 1.5.0
"},{"location":"changelog/#fixed_10","title":"Fixed","text":" - Reading from an option or argument property on a command that hasn\u2019t been invoked will now always throw an
IllegalStateException
"},{"location":"changelog/#310","title":"3.1.0","text":""},{"location":"changelog/#added_9","title":"Added","text":" - Added
required()
and defaultLazy()
for nullable flag options like switch()
. (#240) - Added support for generating autocomplete scripts for Fish shells (#189)
- Added
CompletionCommand
and CliktCommand.completionOption()
that will print an autocomplete script when invoked, as an alternative to using environment variables.
"},{"location":"changelog/#changed_12","title":"Changed","text":" - Updated Kotlin to 1.4.21
@argfiles
now allow line breaks in quoted values, which are included in the value verbatim. You can now end lines with \\
to concatenate them with the following line. (#248)
"},{"location":"changelog/#301","title":"3.0.1","text":""},{"location":"changelog/#deprecated_3","title":"Deprecated","text":" - Deprecated calling
echo
with err
or lineSeparator
but no message
.
"},{"location":"changelog/#300","title":"3.0.0","text":""},{"location":"changelog/#added_10","title":"Added","text":" - Clikt\u2019s JS target now supports both NodeJS and Browsers. (#198)
- Default values for switch options are now shown in the help. Help text can be customized using the
defaultForHelp
argument, similar to normal options. (#205) - Added
FlagOption.convert
(#208) - Added ability to use unicode NEL character (
\\u0085
) to manually break lines in help output (#214) - Added
help(\"\")
extension to options and arguments as an alternative to passing the help as an argument (#207) - Added
valueSourceKey
parameter to option
- Added
check()
extensions to options and arguments as an alternative to validate()
- Added
prompt
and confirm
functions to CliktCommand
that call the TermUi
equivalents with the current console. - Added
echo()
overload with no parameters to CliktCommand that prints a newline by itself. - Added localization support. You can set an implementation of the
Localization
interface on your context with your translations. (#227)
"},{"location":"changelog/#fixed_11","title":"Fixed","text":" - Hidden options will no longer be suggested as possible typo corrections. (#202)
- Options and Arguments with
multiple(required=true)
will now show as required in help output. (#212) - Multiple short lines in a help text paragraph no longer appear dedented (#215)
"},{"location":"changelog/#changed_13","title":"Changed","text":" - Updated Kotlin to 1.4.0
Argument.help
and Option.help
properties have been renamed to argumentHelp
and optionHelp
, respectively. The help
parameter names to option()
and argument()
are unchanged. commandHelp
and commandHelpEpilog
properties on CliktCommand
are now open
, so you can choose to override them instead of passing help
and epilog
to the constructor. - Replaced
MapValueSource.defaultKey
with ValueSource.getKey()
, which is more customizable. Option.metavar
, Option.parameterHelp
, OptionGroup.parameterHelp
and Argument.parameterHelp
properties are now functions. - Changed constructor parameters of
CliktHelpFormatter
. Added localization
and removed usageTitle
, optionsTitle
, argumentsTitle
, commandsTitle
, optionsMetavar
, and commandMetavar
. Those strings are now defined on equivalently named functions on Localization
.
"},{"location":"changelog/#removed_1","title":"Removed","text":" - Removed
envvarSplit
parameter from option()
and convert()
. Option values from environment variables are no longer split automatically. (#177) - Removed public constructors from the following classes:
ProcessedArgument
, OptionWithValues
, FlagOption
, CoOccurringOptionGroup
, ChoiceGroup
, MutuallyExclusiveOptions
. MissingParameter
exception replaced with MissingOption
and MissingArgument
- Removed
Context.helpOptionMessage
. Override Localization.helpOptionMessage
and set it on your context instead.
"},{"location":"changelog/#deprecated_4","title":"Deprecated","text":" @ExperimentalCompletionCandidates
and @ExperimentalValueSourceApi
annotations. These APIs no longer require an opt-in.
"},{"location":"changelog/#280","title":"2.8.0","text":""},{"location":"changelog/#added_11","title":"Added","text":" - Added
error
parameter to PrintMessage
and PrintHelpMessage
. When true
, CliktCommand.main
will exit with status code 1. (#187)
"},{"location":"changelog/#changed_14","title":"Changed","text":" - When
printHelpOnEmptyArgs
is true
and no arguments are present, or when invokeWithoutSubcommand
is false
and no subcommand is present, CliktCommand.main
will now exit with status code 1 rather than 0. restrictTo
now works with any Comparable
value, not just Number
. CliktCommand.main
now accepts Array<out String>
, not just Array<String>
. (#196)
"},{"location":"changelog/#fixed_12","title":"Fixed","text":" - Fixed option values being reset when calling multiple subcommands with
allowMultipleSubcommands=true
(#190)
"},{"location":"changelog/#271","title":"2.7.1","text":""},{"location":"changelog/#fixed_13","title":"Fixed","text":" - Fixed NPE thrown in some cases when using
defaultByName
(#179)
"},{"location":"changelog/#270","title":"2.7.0","text":""},{"location":"changelog/#added_12","title":"Added","text":" - Ability to use custom program exit status codes via
ProgramResult
. inputStream
and outputStream
conversions for options and arguments. (#157 and #159) splitPair
, toMap
, and associate
extensions on option
. (#166) treatUnknownOptionsAsArgs
parameter to CliktCommand
. (#152) defaultByName
function for groupChoice
and groupSwitch
options. (#171)
"},{"location":"changelog/#changed_15","title":"Changed","text":" - Update Kotlin to 1.3.71
- Improved command name inference. Now, a class like
MyAppCommand
will infer its commandName
as my-app
rather than myappcommand
. You can still specify the name manually as before. (#168)
"},{"location":"changelog/#fixed_14","title":"Fixed","text":" - Correctly parse short options with attached values that contain
=
"},{"location":"changelog/#260","title":"2.6.0","text":""},{"location":"changelog/#added_13","title":"Added","text":" registeredSubcommands
, registeredOptions
, registeredArguments
, and registeredParameterGroups
methods on CliktCommand
. - Ability to read default option values from configuration files and other sources. Support for Java property files is built in on JVM, see the
json
sample for an example of reading from other formats. allowMultipleSubcommands
parameter to CliktCommand
that allows you to pass multiple subcommands in the same call. (docs) - Errors from typos in subcommand names will now include suggested corrections. Corrections for options and subcommands are now based on a Jaro-Winkler similarity metric, and can be customized with
Context.correctionSuggestor
"},{"location":"changelog/#changed_16","title":"Changed","text":" - Update Kotlin to 1.3.70
convert
can be called more than once on the same option or argument, including after calls to conversion functions like int
and file
. CliktCommand.toString
now includes the class name - Reverted automatic
~
expansion in file()
and path()
introduced in 2.5.0. If you need this behavior, you can implement it with code like convert { /* expand tidle */ }.file()
"},{"location":"changelog/#deprecated_5","title":"Deprecated","text":" wrapValue
is now deprecated, since convert
can be used in its place instead.
"},{"location":"changelog/#250","title":"2.5.0","text":""},{"location":"changelog/#added_14","title":"Added","text":" - Clikt is now available as a Kotlin Multiplatform Project, supporting JVM, NodeJS, and native Windows, Linux, and macOS.
eagerOption {}
function to more easily register eager options. - Eager options can now be added to option groups in help out by passing a value for
groupName
when creating them. canBeSymlink
parameter to file()
and path()
conversions that can be used to disallow symlinks CliktCommand.eagerOption
to simplify creating custom eager options
"},{"location":"changelog/#changed_17","title":"Changed","text":" - The parameter names of
file()
and path()
conversions have changed. The existing names are deprecated, and can be converted to the new usages with an IntelliJ inspection. Note that if you are calling these functions with unnamed arguments (e.g. file(true, false)
), you\u2019ll need to add argument names in order to remove the deprecation warning.
"},{"location":"changelog/#deprecated_6","title":"Deprecated","text":" - The
CliktCommand.context
property has been deprecated in favor of the new name, currentContext
, to avoid confusion with the CliktCommand.context{}
method. NoRunCliktCommand
was renamed to NoOpCliktCommand
. The existing class is deprecated. (#130)
"},{"location":"changelog/#fixed_15","title":"Fixed","text":" file()
and path()
conversions will now properly expand leading ~
in paths to the home directory for mustExist
, canBeFile
, and canBeDir
checks. The property value is unchanged, and can still begin with a ~
. (#131)
"},{"location":"changelog/#240","title":"2.4.0","text":""},{"location":"changelog/#added_15","title":"Added","text":" CompletionCandidates.Fixed
now has a secondary convenience constructor that take a vararg
of String
s CompletionCadidates.Custom
, which allows you to call other binaries or write a script to generate completions. This class is currently experimental. (#79) Option.wrapValue
and Argument.wrapValue
to make it easier to reuse existing conversion functions. ignoreCase
parameter to choice()
and enum()
conversion functions.
"},{"location":"changelog/#changed_18","title":"Changed","text":" option()
and argument()
now take optional completionCandidates
parameters to override how completion is generated. The constructor and copy
functions of OptionsWithValues
and ProcessedArgument
have changed to support default values. - The overloads of
findObject
(1 2) that take a default value have been renamed findOrSetObject
. The existing names are marked with @Deprecated
, and IntelliJ can convert your call sites automatically. (#110) enum()
parameters now accept case-insensitive values by default. You change this behavior by passing ignoreCase = false
to enum()
(#115)
"},{"location":"changelog/#fixed_16","title":"Fixed","text":" groupChoice
help output now includes the choices in the help output metavar TermUi.edit*
functions could freeze on certain editors (#99, thanks @iampravikant and @sebokopter) - Shell completion can now handle command names with dashes. (#104)
- Arguments with
=
in them could be incorrectly interpreted as options (#106)
"},{"location":"changelog/#230","title":"2.3.0","text":""},{"location":"changelog/#added_16","title":"Added","text":" option().groupSwitch()
, which works like groupChoice()
, but uses a switch()
option rather than a choice()
option. UsageError
now has a statusCode
parameter (which defaults to 1). If you\u2019re using ClicktCommand.main
, the value of statusCode
will be passed to exitProcess
.
"},{"location":"changelog/#changed_19","title":"Changed","text":" - Shell completion code is now printed by throwing a
PrintCompletionMessage
(a subclass of PrintMessage
) rather than calling echo
directly.
"},{"location":"changelog/#220","title":"2.2.0","text":""},{"location":"changelog/#added_17","title":"Added","text":" - Added
enum()
conversion for options and arguments. (#84)
"},{"location":"changelog/#changed_20","title":"Changed","text":" - There are now several ways of preventing @-file expansion
"},{"location":"changelog/#fixed_17","title":"Fixed","text":" - Help output missing items when no help text is specified. (#85)
- Help output not grouping options in groups passed to
groupChoice
. (#88)
"},{"location":"changelog/#210","title":"2.1.0","text":""},{"location":"changelog/#added_18","title":"Added","text":" - Ability to prevent rewrapping individual paragraphs in help output.
- Added parameter
required
to Option.multiple()
to require at least one instance of the option on the command line.
"},{"location":"changelog/#changed_21","title":"Changed","text":" CliktCommand.toString()
now includes the names and values of all parameters and subcommands.
"},{"location":"changelog/#fixed_18","title":"Fixed","text":" - Create subcommand context when
helpOptionNames
is empty. (#64)
"},{"location":"changelog/#200","title":"2.0.0","text":""},{"location":"changelog/#added_19","title":"Added","text":" - Bash autocomplete script generation. A property named
completionCandidates
has been added to Argument
and Option
interfaces, and corresponding parameters have been added to the various implementation constructors, as well as the convert
functions. You can use this to control the values autocomplete that will be suggested. option().split()
, and the corresponding OptionWithValues.valueSplit
. - Marking options as deprecated with
option().deprecated()
- You can manually set the pattern to split envvars on by passing a pattern to the
envvarSplit
parameter of option()
- Option groups, mutually exclusive groups, co-occurring groups, and choice options with groups
- Support for Command line argument files a.k.a. \u201c@-files\u201d
"},{"location":"changelog/#changed_22","title":"Changed","text":" - If multiple
--
tokens are present on the command line, all subsequent occurrences after the first are now parsed as positional arguments. Previously, subsequent --
tokens were skipped. - The
PlaintextHelpFormatter
has been replaced with CliktHelpFormatter
, which is more customizable. See the docs for more info, or the new sample for an example of customizing help output to use ANSI colors. - Some of the properties and constructor parameters for
OptionWithValues
and ProcessedArgument
have changed. - The
OptionDelegate
interface has changed, and GroupableOption
and ParameterHolder
interfaces have been added to work with option groups. - Parameter validation now occurs after all parameter delegates have set their values, so the lambdas passed to
validate
may reference other parameters.
"},{"location":"changelog/#170","title":"1.7.0","text":""},{"location":"changelog/#added_20","title":"Added","text":" printHelpOnEmptyArgs
parameter to CliktCommand
constructor. (#41)
"},{"location":"changelog/#fixed_19","title":"Fixed","text":" - Usage errors now correctly print subcommand names. (#47)
- Arguments with
multiple(required=true)
now report an error if no argument is given on the command line. (#36)
"},{"location":"changelog/#160","title":"1.6.0","text":""},{"location":"changelog/#added_21","title":"Added","text":" .multiple().unique()
modifier for options and arguments.
"},{"location":"changelog/#fixed_20","title":"Fixed","text":" - Support multi-line input when redirecting stdin
"},{"location":"changelog/#150","title":"1.5.0","text":""},{"location":"changelog/#added_22","title":"Added","text":" - Ability to use alternate output streams rather than stdin and stdout by setting
Context.console
or by passing a console to TermUI
functions.
"},{"location":"changelog/#140","title":"1.4.0","text":""},{"location":"changelog/#added_23","title":"Added","text":" path()
type for parameter values
"},{"location":"changelog/#changed_23","title":"Changed","text":" - Clikt now targets JVM 8 bytecode
- Responses to
TermUi.confirm()
are now case-insensitive
"},{"location":"changelog/#130","title":"1.3.0","text":""},{"location":"changelog/#added_24","title":"Added","text":" defaultLazy
extension for options and arguments
"},{"location":"changelog/#changed_24","title":"Changed","text":" main
now prints messages to stderr instead of stdout
"},{"location":"changelog/#fixed_21","title":"Fixed","text":" - Parameter help messages are now wrapped more consistently
"},{"location":"changelog/#120","title":"1.2.0","text":""},{"location":"changelog/#added_25","title":"Added","text":" - Default parameter to
option().default()
"},{"location":"changelog/#changed_25","title":"Changed","text":" - Treat tokens with unknown prefixes as arguments (this makes it easier to pass in file paths without using
--
).
"},{"location":"changelog/#110","title":"1.1.0","text":""},{"location":"changelog/#added_26","title":"Added","text":" List<String>
overloads to CliktCommand.parse
and main
err
parameter to TermUi.echo
error
property to Abort
"},{"location":"commands/","title":"Commands","text":"Clikt supports arbitrarily nested commands. You can add one command as a child of another with the subcommands
function, which can be called either in an init
block, or on an existing instance.
"},{"location":"commands/#executing-nested-commands","title":"Executing Nested Commands","text":"For commands with no children, run
is called whenever the command line is parsed (unless parsing is aborted from an error or an option like --help
).
If a command has children, this isn\u2019t the case. Instead, its run
is called only if a child command is invoked, just before the subcommand\u2019s run
. If a parent command is called without specifying a subcommand, the help page is printed and run
is not called.
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\nval verbose by option().flag(\"--no-verbose\")\noverride fun run() {\necho(\"Verbose mode is ${if (verbose) \"on\" else \"off\"}\")\n}\n}\n\nclass Execute : CliktCommand() {\noverride fun run() {\necho(\"executing\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n --verbose / --no-verbose\n -h, --help Show this message and exit\n\nCommands:\n execute\n
$ ./tool --verbose execute\nVerbose mode is on\nexecuting\n
"},{"location":"commands/#customizing-command-name","title":"Customizing Command Name","text":"The default name for subcommands is inferred as a lowercase name from the command class name. You can also set a name manually in the CliktCommand
constructor.
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\noverride fun run()= Unit\n}\n\nclass Execute : CliktCommand(name = \"RUN-ME\") {\noverride fun run() {\necho(\"executing\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool RUN-ME\nexecuting\n
$ ./tool -h\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n RUN-ME\n
"},{"location":"commands/#passing-parameters","title":"Passing Parameters","text":"When calling subcommands, the position of options and arguments on the command line affect which command will parse them. A parameter is parsed by a command if it occurs after the command name, but before any other command names.
ExampleUsage class Tool : CliktCommand(help = \"A tool that runs\") {\nval verbose by option().flag(\"--no-verbose\")\noverride fun run() = Unit\n}\n\nclass Execute : CliktCommand(help = \"Execute the command\") {\nval name by option()\noverride fun run() = Unit\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool --help\nUsage: tool [<options>] <command> [<args>]...\n\n A tool that runs\n\nOptions:\n --verbose / --no-verbose\n -h, --help Show this message and exit\n\nCommands:\n execute Execute the command\n
If you instead execute --help
after the subcommand, the subcommand\u2019s help is printed:
$ ./tool execute --help\nUsage: execute [<options>]\n\n Execute the command\n\nOptions:\n --name <text>\n -h, --help Show this message and exit\n
But executing ./tool --help execute
, with the option before the subcommand, will cause the parent\u2019s help option to be invoked, printing out Tool
\u2018s help page as if you just typed ./tool --help
.
"},{"location":"commands/#nested-handling-and-contexts","title":"Nested Handling And Contexts","text":"Normally nested command are independent of each other: a child can\u2019t access its parent\u2019s parameters. This makes composing commands much easier, but what if you want to pass information to a child command? You can do so with the command\u2019s Context
.
Every time the command line is parsed, each command creates a new context object for itself that is linked to its parent\u2019s context. Context
objects have a number of properties that can be used to customize command line parsing. Although each command creates its own context, the configuration is inherited from the parent context.
Context
objects also have an obj
property that can hold an object that can be accessed from child commands.
ExampleUsage class Tool : CliktCommand() {\nval verbose by option().flag(\"--no-verbose\")\nval config by findOrSetObject { mutableMapOf<String, String>() }\noverride fun run() {\nconfig[\"VERBOSE\"] = if (verbose) \"on\" else \"off\"\n}\n}\n\nclass Execute : CliktCommand() {\nval config by requireObject<Map<String, String>>()\noverride fun run() {\necho(\"Verbose mode is ${config[\"VERBOSE\"]}\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool --verbose execute\nVerbose mode is on\n
The findObject
, findOrSetObject
, and requireObject
functions will walk up the context tree until they find a obj
with the given type. If no such object exists, they will either return null
, throw an exception, or create an instance of the object and store it on the command\u2019s context, depending on which function you use. Since each context only has a single obj
, if you need to store multiple objects on a single context, you could create a data class with everything you want to store and set that as your obj
.
Note that the findOrSetObject
property is lazy and won\u2019t set the Context\u2019s obj
until its value is accessed. If you need to set an object for subcommands without accessing the property, you should use currentContext.findOrSetObject
, or set currentContext.obj
or Context.Builder.obj
directly, instead.
Eager initialization with findOrSetObjectEager initialization with currentContext.objEager initialization with context builder class Tool : CliktCommand() {\noverride fun run() {\n// runs eagerly\ncurrentContext.findOrSetObject { MyConfig() }\n}\n}\n
class Tool : CliktCommand() {\noverride fun run() {\n// runs eagerly, won't look for parent contexts\ncurrentContext.obj = MyConfig()\n}\n}\n
Tool().context {\n// runs eagerly, won't look for parent contexts\nobj = MyConfig()\n}\n
"},{"location":"commands/#running-parent-command-without-children","title":"Running Parent Command Without Children","text":"Normally, if a command has children, run
is not called unless a child command is invoked on the command line. Instead, --help
is called on the parent. If you want to change this behavior to always call run()
on the parent, you can do so by setting invokeWithoutSubcommand
to true
. The Context
will then have information on the subcommand that is about to be invoked, if there is one.
ExampleUsage 1Usage 2 class Tool : CliktCommand(invokeWithoutSubcommand = true) {\noverride fun run() {\nval subcommand = currentContext.invokedSubcommand\nif (subcommand == null) {\necho(\"invoked without a subcommand\")\n} else {\necho(\"about to run ${subcommand.commandName}\")\n}\n}\n}\n\nclass Execute : CliktCommand() {\noverride fun run() {\necho(\"running subcommand\")\n}\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Execute()).main(args)\n
$ ./tool\ninvoked without a subcommand\n
$./tool execute\nabout to run execute\nrunning subcommand\n
"},{"location":"commands/#customizing-contexts","title":"Customizing Contexts","text":"Contexts have a number of properties that can be customized, and which are inherited by child commands. You can change these properties with the context
builder function, which can be called in an init
block, or on a command instance.
For example, you can change the name of help option. These definitions are equivalent:
Version 1Version 2Usage class Cli : NoOpCliktCommand() {\ninit {\ncontext { helpOptionNames = setOf(\"/help\") }\n}\n}\nfun main(args: Array<String>) = Cli()\n
class Cli : NoOpCliktCommand()\nfun main(args: Array<String>) = Cli()\n.context { helpOptionNames = setOf(\"/help\") }\n.main(args)\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n -h, --help print the help\n
"},{"location":"commands/#printing-the-help-message-when-no-arguments-are-given","title":"Printing the Help Message When No Arguments Are Given","text":"Normally, if a command is called with no values on the command line, a usage error is printed if there are required parameters, or run
is called if there aren\u2019t any.
You can change this behavior by passing printHelpOnEmptyArgs = true
to your command\u2019s constructor. This will cause a help message to be printed when no values are provided on the command line, regardless of the parameters in your command.
ExampleUsage class Cli : CliktCommand(printHelpOnEmptyArgs = true) {\nval arg by argument()\noverride fun run() { echo(\"Command ran\") }\n}\n
$ ./cli\nUsage: cli [<options>]\n\nOptions:\n -h, --help print the help\n
"},{"location":"commands/#warnings-and-other-messages","title":"Warnings and Other Messages","text":"When you want to show information to the user, you\u2019ll usually want to use the functions for printing to stdout directly.
However, there\u2019s another mechanism that can be useful when writing reusable parameter code: command messages. These messages are buffered during parsing and printed all at once immediately before a command\u2019s run
is called. They are not printed if there are any errors in parsing. This type of message is used by Clikt for deprecating options
.
You can issue a command message by calling CliktCommand.issueMessage
or with the message
function available in the context of parameter transformers.
ExampleUsage 1Usage 2 class Cli : CliktCommand() {\n// This will print the warning when the option is given, but not if there are errors\nval opt by option().validate {\nif (it.isEmpty()) message(\"Empty strings are not recommended\")\n}\noverride fun run() {\necho(\"command run\")\n}\n}\n
$ ./cli --opt=''\nEmpty strings are not recommended\ncommand run\n
$ ./cli --opt='' --oops\nError: no such option: \"--oops\".\n
You can disable automatic message printing on the command\u2019s context:
ExampleUsage class Cli : CliktCommand() {\ninit { context { printExtraMessages = false } }\nval opt by option().validate {\nif (it.isEmpty()) message(\"Empty strings are not recommended\")\n}\noverride fun run() {\necho(\"command run\")\n}\n}\n
$ ./cli --opt=''\ncommand run\n
"},{"location":"commands/#chaining-and-repeating-subcommands","title":"Chaining and Repeating Subcommands","text":"Some command line interfaces allow you to call more than one subcommand at a time. For example, you might do something like gradle clean build publish
to run the clean
task, then the build
task, then the publish
task, which are all subcommands of gradle
.
To do this with Clikt, pass allowMultipleSubcommands = true
to your CliktCommand
constructor.
ExampleUsage class Compiler: CliktCommand(allowMultipleSubcommands = true) {\noverride fun run() {\necho(\"Running compiler\")\n}\n}\n\nclass Clean: CliktCommand() {\nval force by option().flag()\noverride fun run() {\necho(\"Cleaning (force=$force)\")\n}\n}\n\nclass Build: CliktCommand() {\nval file by argument().file()\noverride fun run() {\necho(\"Building $file\")\n}\n}\n\nfun main(args: Array<String>) = Compiler().subcommands(Clean(), Build()).main(args)\n
$ ./compiler clean --force build main.kt\nRunning compiler\nCleaning (force=true)\nBuilding main.kt\n
The parent command will run
once, and each subcommand will run
once each time they\u2019re called.
"},{"location":"commands/#parsing-multiple-subcommands","title":"Parsing multiple subcommands","text":"Note that enabling allowMultipleSubcommands
will disable allowInterspersedArgs
on the command and all its subcommands. If both were allowed to be enabled at the same time, then not all command lines could be parsed unambiguously.
When parsing in this mode, tokens are consumed greedily by a subcommand until it encounters an argument token it doesn\u2019t support, at which point the parent command resumes parsing where the subcommand left off. This means that if you have a subcommand with an argument().multiple()
parameter, you won\u2019t be able to call any other subcommands after that one, since it will consume the rest of the command line.
Subcommands of a command with allowMultipleSubcommands=true
can themselves have subcommands, but cannot have allowMultipleSubcommands=true
.
"},{"location":"documenting/","title":"Documenting Scripts","text":"Clikt takes care of creating formatted help messages for commands. There are a number of ways to customize the default behavior. You can also implement your own HelpFormatter
and set it on the command\u2019s context.
"},{"location":"documenting/#help-texts","title":"Help Texts","text":"Commands and parameters accept a help
argument. Commands also accept an epilog
argument, which is printed after the parameters and commands on the help page. All text is automatically trimmed of leading indentation and re-wrapped to the terminal width.
As an alternative to passing your help strings as function arguments, you can also use the help()
extensions for your options, and override commandHelp
and commandHelpEpilog
on your commands. These methods can access the terminal theme on the context to add color to your help text.
ExampleAlternate styleHelp output class Hello : CliktCommand(help = \"\"\"\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n \"\"\"\n) {\nval count by option(\"-c\", \"--count\", metavar=\"count\", help = \"number of greetings\").int().default(1)\nval name by argument()\noverride fun run() = repeat(count) { echo(\"Hello $name!\") }\n}\n
class Hello : CliktCommand() {\noverride fun commandHelp(context: Context): String {\nval style = context.theme.info\nreturn \"\"\"\n This script prints ${style(\"<name>\")} ${style(\"<count>\")} times.\n\n${style(\"<count>\")} must be a positive number, and defaults to 1.\n \"\"\".trimIndent()\n}\n\nval count by option(\"-c\", \"--count\", metavar=\"count\").int().default(1)\n.help { theme.success(\"number of greetings\") }\nval name by argument()\noverride fun run() = repeat(count) { echo(\"Hello $name!\") }\n}\n
$ ./hello --help\nUsage: hello [<options>] <name>\n\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n\nOptions:\n -c, --count <count> number of greetings\n -h, --help Show this message and exit\n
Option names and metavars will appear in help output even if no help string is specified for them. On the other hand, arguments only appear in the usage string. It is possible to add a help string to arguments which will be added to the help page, but the Unix convention is to just describe arguments in the command help.
"},{"location":"documenting/#markdown-in-help-texts","title":"Markdown in help texts","text":"All help texts use Mordant to render Markdown. You can use all the normal markdown features, such as lists, tables, and even hyperlinks if your terminal supports them.
ExampleHelp output class Tool : NoOpCliktCommand() {\nval option by option().help {\n\"\"\"\n | This | is | a | table |\n | ---- | -- | - | ----- |\n | 1 | 2 | 3 | 4 |\n\n - This is\n - a list\n\n ```\n You can\n use code blocks\n ```\n \"\"\".trimIndent()\n}\n}\n
Usage: tool [<options>]\n\nOptions:\n --option=<text> \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 This \u2502 is \u2502 a \u2502 table \u2502\n \u255e\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561\n \u2502 1 \u2502 2 \u2502 3 \u2502 4 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n \u2022 This is\n \u2022 a list\n\n \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n \u2502You can \u2502\n \u2502 use code blocks\u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n -h, --help Show this message and exit\n
"},{"location":"documenting/#manual-line-breaks","title":"Manual Line Breaks","text":"If you want to insert a line break manually without preformatting the entire paragraph, you can use the Unicode Next Line (NEL) character. You can type a NEL with the unicode literal \\u0085
.
Clikt will treat NEL similarly to how <br>
behaves in HTML: The NEL will be replaced with a line break in the output, and the paragraph will still be wrapped to the terminal width.
ExampleHelp output class Tool : NoOpCliktCommand() {\nval option by option()\n.help(\"This help will be at least two lines.\\u0085(this will start a new line)\")\n}\n
Usage: tool\n\nOptions:\n --option This help will be at least\n two lines.\n (this will start a new\n line)\n -h, --help Show this message and exit\n
Tip
In raw multiline strings (which do not parse escape sequences), you\u2019ll need to insert the NEL with a string template such as ${\"\\u0085\"}
.
"},{"location":"documenting/#subcommand-short-help","title":"Subcommand Short Help","text":"Subcommands are listed in the help page based on their name. They have a short help string which is the first line of their help.
ExampleUsage class Tool : NoOpCliktCommand()\n\nclass Execute : NoOpCliktCommand(help = \"\"\"\n Execute the command.\n\n The command will be executed.\n \"\"\")\n\nclass Abort : NoOpCliktCommand(help=\"Kill any running commands.\")\n
$ ./tool --help\nUsage: tool [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n execute Execute the command.\n abort Kill any running commands.\n
"},{"location":"documenting/#help-option-customization","title":"Help Option Customization","text":"Clikt handles the help option is specially. It is added automatically to every command. Any help option name that conflicts with another option is not used for the help option. If the help option has no unique names, it is not added.
You can change the help option\u2019s name and help message on the command\u2019s context:
ExampleUsage class HelpLocalization: Localization {\noverride fun helpOptionMessage(): String = \"show the help\"\n}\n\nclass Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpOptionNames = setOf(\"/help\")\nlocalization = HelpLocalization()\n}\n}\n}\n
$ ./tool /help\nUsage: tool [<options>]\n\nOptions:\n /help show the help\n
If you don\u2019t want a help option to be added, you can set helpOptionNames = emptySet()
"},{"location":"documenting/#default-values-in-help","title":"Default Values in Help","text":"You can configure the help formatter to show default values in the help output by passing showDefaultValues = true
to the MordantHelpFormatter
. By default, the string value of the default value will be shown. You can show a different value by passing the value you want to show to the defaultForHelp
parameter of default
.
ExampleUsage class Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, showDefaultValues = true) }\n}\n}\n\nval a by option(help = \"this is optional\").default(\"value\")\nval b by option(help = \"this is also optional\").default(\"value\", defaultForHelp=\"chosen for you\")\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --a <text> this is optional (default: value)\n --b <text> this is also optional (default: chosen for you)\n
"},{"location":"documenting/#required-options-in-help","title":"Required Options in Help","text":"By default, required
options are displayed the same way as other options. The help formatter includes two different ways to show that an option is required.
"},{"location":"documenting/#required-option-marker","title":"Required Option Marker","text":"You can pass a character to the requiredOptionMarker
argument of the MordantHelpFormatter
.
ExampleUsage class Tool : NoOpCliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, requiredOptionMarker = \"*\") }\n}\n}\n\nval option by option(help = \"this is optional\")\nval required by option(help = \"this is required\").required()\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --option <text> this is optional\n* --required <text> this is required\n -h, --help Show this message and exit\n
"},{"location":"documenting/#required-option-tag","title":"Required Option Tag","text":"You can also show a tag for required options by passing showRequiredTag = true
to the MordantHelpFormatter
.
ExampleUsage class Tool : CliktCommand() {\ninit {\ncontext {\nhelpFormatter = { MordantHelpFormatter(it, showRequiredTag = true) }\n}\n}\n\nval option by option(help = \"this is optional\")\nval required by option(help = \"this is required\").required()\n}\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n --option <text> this is optional\n --required <text> this is required (required)\n -h, --help Show this message and exit\n
"},{"location":"documenting/#grouping-options-in-help","title":"Grouping Options in Help","text":"You can group options into separate help sections by using OptionGroup and importing groups.provideDelegate. The name of the group will be shown in the output. You can also add an extra help message to be shown with the group. Groups can\u2019t be nested.
ExampleUsage import com.github.ajalt.clikt.parameters.groups.provideDelegate\n\nclass UserOptions : OptionGroup(\nname = \"User Options\",\nhelp = \"Options controlling the user\"\n) {\nval name by option(help = \"user name\")\nval age by option(help = \"user age\").int()\n}\n\nclass Tool : NoOpCliktCommand() {\nval userOptions by UserOptions()\n}\n
$ ./tool --help\nUsage: cli [<options>]\n\nUser Options:\n\n Options controlling the user\n\n --name <text> user name\n --age <int> user age\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"documenting/#suggesting-corrections-for-mistyped-parameters","title":"Suggesting Corrections for Mistyped Parameters","text":"When an option or subcommand is mistyped, Clikt will suggest corrections that are similar to the typed value.
Mistyped OptionMistyped Subcommand $ ./cli --sise=5\nError: no such option: \"--sise\". Did you mean \"--size\"?\n
$ ./cli building\nUsage: cli [<options>] <command> [<args>]...\n\nError: no such subcommand: \"building\". Did you mean \"build\"?\n
By default, Clikt will suggest corrections of any similar option or subcommand name based on a similarity metric. You can customize the suggestions by setting a correctionSuggestor
on your command\u2019s context.
class Cli : NoOpCliktCommand() {\ninit {\ncontext {\n// Only suggest corrections that start with the entered value\ncorrectionSuggestor = { enteredValue, possibleValues ->\npossibleValues.filter { it.startsWith(enteredValue) }\n}\n}\n}\n}\n
"},{"location":"documenting/#localization","title":"Localization","text":"You can localize error messages by implementing Localization
and setting the localization
property on your context.
ExampleUsage class CursiveLocalization : Localization {\noverride fun usageTitle() = \"\ud835\udcb0\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52:\"\noverride fun optionsTitle() = \"\ud835\udcaa\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8\"\noverride fun optionsMetavar() = \"\ud835\udc5c\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8\"\noverride fun helpOptionMessage() = \"\ud835\udcae\ud835\udcbd\ud835\udc5c\ud835\udccc \ud835\udcc9\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc2\ud835\udc52\ud835\udcc8\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52 \ud835\udcb6\ud835\udcc3\ud835\udcb9 \ud835\udc52\ud835\udccd\ud835\udcbe\ud835\udcc9\"\n\n// ... override the rest of the strings here\n}\n\nclass I18NTool : NoOpCliktCommand(help = \"\ud835\udcaf\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc9\ud835\udc5c\ud835\udc5c\ud835\udcc1 \ud835\udcbe\ud835\udcc8 \ud835\udcbe\ud835\udcc3 \ud835\udcb8\ud835\udcca\ud835\udcc7\ud835\udcc8\ud835\udcbe\ud835\udccb\ud835\udc52\") {\ninit {\ncontext { localization = CursiveLocalization() }\n}\n}\n
$ ./i18ntool --help\n\ud835\udcb0\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52: i18ntool [<\ud835\udc5c\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8>]\n\n \ud835\udcaf\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc9\ud835\udc5c\ud835\udc5c\ud835\udcc1 \ud835\udcbe\ud835\udcc8 \ud835\udcbe\ud835\udcc3 \ud835\udcb8\ud835\udcca\ud835\udcc7\ud835\udcc8\ud835\udcbe\ud835\udccb\ud835\udc52\n\n\ud835\udcaa\ud835\udcc5\ud835\udcc9\ud835\udcbe\ud835\udc5c\ud835\udcc3\ud835\udcc8:\n -h, --help \ud835\udcae\ud835\udcbd\ud835\udc5c\ud835\udccc \ud835\udcc9\ud835\udcbd\ud835\udcbe\ud835\udcc8 \ud835\udcc2\ud835\udc52\ud835\udcc8\ud835\udcc8\ud835\udcb6\ud835\udc54\ud835\udc52 \ud835\udcb6\ud835\udcc3\ud835\udcb9 \ud835\udc52\ud835\udccd\ud835\udcbe\ud835\udcc9\n
"},{"location":"exceptions/","title":"Exception Handling","text":"Clikt uses exceptions internally to signal that processing has ended early for any reason. This includes incorrect command line usage, or printing a help page.
"},{"location":"exceptions/#where-are-exceptions-handled","title":"Where are Exceptions Handled?","text":"When you call CliktCommand.main
, it will parse the command line and catch any CliktError
exceptions. If it catches one, it will then print out the error\u2019s message and exit the process with the error\u2019s statusCode
. The message is printed to stderr or stdout depending on the error\u2019s printError
property.
For exceptions raised by Clikt, PrintMessage
and PrintHelpMessage
have a statusCode
of 0 and print to stdout. Other exceptions have a statusCode
of 1 and print to stderr.
Any other types of exceptions indicate a programming error, and are not caught by main
. However, convert
and the other parameter transformations will wrap exceptions thrown inside them in a UsageError
, so if you define a custom transformation, you don\u2019t have to worry about an exception escaping to the user.
"},{"location":"exceptions/#handling-exceptions-manually","title":"Handling Exceptions Manually","text":"CliktCommand.main
is just a try
/catch
block surrounding CliktCommand.parse
, so if you don\u2019t want exceptions to be caught, you can call parse
wherever you would normally call main
.
Tip
You can use echoFormattedHelp to print the help or error message to any exception, or getFormattedHelp to get the help message as a string.
fun main(args: Array<String>) {\nval cli = Cli()\ntry {\ncli.parse(args)\n} catch (e: CliktError) {\ncli.echoFormattedHelp(e)\nexitProcess(e.statusCode)\n}\n}\n
"},{"location":"exceptions/#which-exceptions-exist","title":"Which Exceptions Exist?","text":"All exceptions thrown by Clikt are subclasses of CliktError
.
The following subclasses exist:
Abort
: The command should exit immediately with the given statusCode
without printing any messages. PrintMessage
: The exception\u2019s message should be printed. PrintHelpMessage
: The help page for the exception\u2019s command should be printed. PrintCompletionMessage
: Shell completion code for the command should be printed. UsageError
: The command line was incorrect in some way. All the following exceptions subclass from this. These exceptions are automatically augmented with extra information about the current parameter, if possible. MultiUsageError
: Multiple UsageError
s occurred. The errors
property contains the list of the errors. ProgramResult
: The program should exit with the statusCode
from this exception. BadParameterValue
: A parameter was given the correct number of values, but of invalid format or type. MissingOption
and MissingArgument
: A required parameter was not provided. NoSuchOption
: An option was provided that does not exist. NoSuchSubcommand
: A subcommand was called that does not exist. IncorrectOptionValueCount
: An option was supplied but the number of values supplied to the option was incorrect. IncorrectArgumentValueCount
: An argument was supplied but the number of values supplied was incorrect. MutuallyExclusiveGroupException
: Multiple options in a mutually exclusive group were supplied when the group is restricted to a single value. FileNotFound
: A required configuration file or @-file was not found. InvalidFileFormat
: A configuration file or @-file failed to parse correctly.
"},{"location":"migration/","title":"Upgrading to Newer Releases","text":""},{"location":"migration/#upgrading-to-40","title":"Upgrading to 4.0","text":""},{"location":"migration/#help-formatting","title":"Help formatting","text":"The CliktHelpFormatter
class has been removed and replaced with the MordantHelpFormatter
. The MordantHelpFormatter
constructor takes a Context
instead of a Localization
, and the parameters controlling size and spacing have been removed. See the documentation for details on how to set the help formatter on the Context.
If you were subclassing CliktHelpFormatter
, MordantHelpFormatter
\u2018s open methods are different. See the helpformat
sample for an example of how to use the new formatter.
"},{"location":"migration/#prompting","title":"Prompting","text":"The CliktConsole
class has been removed. If you were using it, use your command\u2019s Mordant terminal
instead.
The prompt
and confirm
methods now use Mordant\u2019s prompting functionality, and some of their arguments have changed. In particular, conversion lambdas now return a ConversionResult
instead of throwing an exception.
In 4.0In 3.0 val input = prompt(\"Enter a number\") {\nit.toIntOrNull()\n?.let { ConversionResult.Valid(it) }\n?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\n
val input = prompt(\"Enter a number\") {\nit.toIntOrNull() ?: throw BadParameterValue(\"$it is not a valid integer\")\n}\n
"},{"location":"migration/#upgrading-to-30","title":"Upgrading to 3.0","text":""},{"location":"migration/#maven-coordinates","title":"Maven Coordinates","text":"Clikt\u2019s Maven groupId changed from com.github.ajalt
to com.github.ajalt.clikt
. So the full coordinate is now com.github.ajalt.clikt:clikt:3.0.0
.
With the new Multiplatform plugin in Kotlin 1.4, there is no longer a separate clikt-multiplatform
artifact. You can use com.github.ajalt.clikt:clikt:3.0.0
for both JVM-only and Multiplatform projects.
"},{"location":"migration/#environment-variable-splitting","title":"Environment variable splitting","text":"There used to be an envvarSplit
parameter to option()
and its convert()
that would split values coming from an environment variable. This parameter is removed, and values from environment variables are no longer split automatically.
If you still want to split option values, you can do so explicitly with split()
.
"},{"location":"migration/#experimental-apis","title":"Experimental APIs","text":"The Value Source API and Completion Generation APIs no longer require opt-in. You can use these APIs without needing the ExperimentalValueSourceApi
or ExperimentalCompletionCandidates
annotations.
"},{"location":"migration/#localization","title":"Localization","text":"By default, all strings are defined in the Localization
object set on your context.
This means that string parameters like usageTitle
in the constructor for CliktHelpFormatter
have been removed in favor of functions like Localization.usageTitle()
.
Context.helpOptionMessage
has also been removed in favor of Localization.helpOptionMessage()
. See Help Option Customization for an example.
"},{"location":"options/","title":"Options","text":"Options are added to commands by defining a property delegate with the option
function.
"},{"location":"options/#basic-options","title":"Basic Options","text":"The default option takes one value of type String
. The property is nullable. If the option is not given on the command line, the property value will be null. If the option is given at least once, the property will return the value of the last occurrence of the option.
ExampleUsage class Hello: CliktCommand() {\nval name by option(help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello --name=Foo\nHello, Foo!\n
"},{"location":"options/#option-names","title":"Option Names","text":"If you don\u2019t specify names for an option, a lowercase hyphen-separated name is automatically inferred from the property. For example, val myOpt by option()
will create an option that can be called with --my-opt
.
You can also specify any number of names for an option manually:
class Hello: CliktCommand() {\nval name by option(\"-n\", \"--name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
Option names that are two characters long (like -n
) are treated as POSIX-style short options. You call them with a value like this:
Usage 1Usage 2 $ ./hello -nfoo\nHello, foo!\n
$ ./hello -n foo\nHello, foo!\n
All other option names are considered long options, and can be called like this:
Usage 1Usage 2 $ ./hello --name=foo\nHello, foo!\n
$ ./hello --name foo\nHello, foo!\n
"},{"location":"options/#customizing-options","title":"Customizing Options","text":"The option behavior and delegate type can be customized by calling extension functions on the option
call. For example, here are some different option declarations:
val a: String? by option()\nval b: Int? by option().int()\nval c: Pair<Int, Int>? by option().int().pair()\nval d: Pair<Int, Int> by option().int().pair().default(0 to 0)\nval e: Pair<Float, Float> by option().float().pair().default(0f to 0f)\n
There are three main types of behavior that can be customized independently:
- The type of each value in the option. The value type is
String
by default, but can be customized with built-in functions like int
or choice
, or manually with convert
. This is detailed in the parameters page. - The number of values that the option requires. Options take one value by default, but this can be changed with built-in functions like
pair
and triple
, or manually with transformValues
. - How to handle all calls to the option (i.e. if the option is not present, or is present more than once). By default, the option delegate value is the null if the option is not given on the command line, and will use the value of the last occurrence if the option is given more than once. You can change this behavior with functions like
default
and multiple
.
Since the three types of customizations are orthogonal, you can choose which ones you want to use, and if you implement a new customization, it can be used with all the existing functions without any repeated code.
"},{"location":"options/#default-values","title":"Default Values","text":"By default, option delegates return null
if the option wasn\u2019t provided on the command line. You can instead return a default value with default
.
ExampleUsage 1Usage 2 class Pow : CliktCommand() {\nval exp by option(\"-e\", \"--exp\").double().default(1.0)\noverride fun run() {\necho(\"2 ^ $exp = ${(2.0).pow(exp)}\")\n}\n}\n
$ ./pow -e 8\n2 ^ 8.0 = 256.0\n
$ ./pow\n2 ^ 1.0 = 2.0\n
If the default value is expensive to compute, or you want to use another parameter as the default, you can use defaultLazy
instead of default
. It has the same effect, but you give it a lambda returning the default value, and the lambda will only be called if the option isn\u2019t present on the command line.
Warning
The lambda you pass to defaultLazy
is normally called at most once. But the lambda references another parameter, it may be called more than once in order to make sure the parameter it references is available.
"},{"location":"options/#multi-value-options","title":"Multi Value Options","text":"Options can take a fixed number of values separated by whitespace, a variable number of values separated by a delimiter, or a variable number of values separated by a whitespace.
"},{"location":"options/#options-with-fixed-number-of-values","title":"Options With Fixed Number of Values","text":"There are built in functions for options that take two values (pair
, which uses a Pair
), or three values (triple
, which uses a Triple
). You can change the type of each value as normal with functions like int
.
If you need more values, you can provide your own container with transformValues
. You give that function the number of values you want, and a lambda that will transform a list of values into the output container. The list will always have a size equal to the number you specify. If the user provides a different number of values, Clikt will inform the user and your lambda won\u2019t be called.
ExampleUsage data class Quad<out T>(val a: T, val b: T, val c: T, val d: T)\nfun <T> Quad<T>.toList(): List<T> = listOf(a, b, c, d)\n\nclass Geometry : CliktCommand() {\nval square by option().int().pair()\nval cube by option().int().triple()\nval tesseract by option().int().transformValues(4) { Quad(it[0], it[1], it[2], it[3]) }\noverride fun run() {\necho(\"Square has dimensions ${square?.toList()?.joinToString(\"x\")}\")\necho(\"Cube has dimensions ${cube?.toList()?.joinToString(\"x\")}\")\necho(\"Tesseract has dimensions ${tesseract?.toList()?.joinToString(\"x\")}\")\n}\n}\n
$ ./geometry --square 1 2 --cube 3 4 5 --tesseract 6 7 8 9\nSquare has dimensions 1x2\nCube has dimensions 3x4x5\nTesseract has dimensions 6x7x8x9\n
"},{"location":"options/#splitting-an-option-value-on-a-delimiter","title":"Splitting an Option Value on a Delimiter","text":"You can use split
to allow a variable number of values to a single option invocation by separating the values with non-whitespace delimiters. This will also split values from environment variables.
ExampleUsageUsage with Environment Variable class C : CliktCommand() {\nval profiles by option(\"-P\", envvar=\"PROFILES\").split(\",\")\noverride fun run() {\nfor (profile in profiles) {\necho(profile)\n}\n}\n}\n
$ ./split -P profile-1,profile-2\nprofile-1\nprofile-2\n
$ export PROFILES=profile-1,profile-2\n$ ./split\nprofile-1\nprofile-2\n
"},{"location":"options/#options-with-an-optional-value","title":"Options with an Optional Value","text":"You can create options that take zero or one values with optionalValue
or optionalValueLazy
.
ExampleUsage with no option valueUsage with option valueUsage with no option class C : CliktCommand() {\nval log by option().optionalValue(\"debug\").default(\"none\")\noverride fun run() {\necho(\"log level: $log\")\n}\n}\n
$ ./command --log\nlog level: debug\n
$ ./command --log=verbose\nlog level: verbose\n
$ ./command\nlog level: none\n
Warning
If a user specifies the value without an =
, as in --log debug
, the debug
will always be interpreted as a value for the option, even if the command accepts arguments. This might be confusing to your users, so you can require that the option value always be specified with =
by passing optionalValue(acceptsUnattachedValue=false)
.
"},{"location":"options/#options-with-a-variable-number-of-values","title":"Options With a Variable Number of Values","text":"If you want your option to take a variable number of values, but want to split the value on whitespace [rather than a delimiter][#splitting-an-option-value-on-a-delimiter], you can use varargValues
.
ExampleUsage class Order : CliktCommand() {\nval sizes: List<String> by option().varargValues()\noverride fun run() {\necho(\"You ordered: $sizes\")\n}\n}\n
$ ./order --sizes small medium\nYou ordered: [\"small\", \"medium\"]\n
By default, varargValues
requires at least one value, and has no maximum limit. You can configure the limits by passing min
and max
arguments to varargValues
.
"},{"location":"options/#multiple-options","title":"Multiple Options","text":"Normally, when an option is provided on the command line more than once, only the values from the last occurrence are used. But sometimes you want to keep all values provided. For example, git commit -m foo -m bar
would create a commit message with two lines: foo
and bar
. To get this behavior with Clikt, you can use multiple
. This will cause the property delegate value to be a list, where each item in the list is the value of from one occurrence of the option. If the option is never given, the list will be empty (or you can specify a default to use).
ExampleUsage class Commit : CliktCommand() {\nval message: List<String> by option(\"-m\").multiple()\noverride fun run() {\necho(message.joinToString(\"\\n\"))\n}\n}\n
$ ./commit -m foo -m bar\nfoo\nbar\n
You can combine multiple
with item type conversions and multiple values.
val opt: List<Pair<Int, Int>> by option().int().pair().multiple()\n
"},{"location":"options/#default-values-for-optionmultiple","title":"Default values for option().multiple()","text":"You can also supply a default value to multiple
or require at least one value be present on the command line. These are specified as arguments rather than with separate extension functions since they don\u2019t change the type of the delegate.
RequiredDefault val opt: List<String> by option().multiple(required=true)\n
val opt: List<String> by option().multiple(default=listOf(\"default message\"))\n
"},{"location":"options/#deduplicating-optionmultiple-into-a-unique-set","title":"Deduplicating option().multiple() into a unique set","text":"You can discard duplicate values from a multiple
option with unique
.
ExampleUsage class Build : CliktCommand() {\nval platforms: Set<String> by option(\"-p\").multiple().unique()\noverride fun run() {\necho(\"Building for platforms: $platforms\")\n}\n}\n
$ ./build -p android -p ios -p android\nBuilding for platforms: [android, ios]\n
"},{"location":"options/#key-value-and-map-options","title":"Key-Value and Map Options","text":"You can split an option\u2019s value into a key-value pair with splitPair
. By default, the delimiter =
will be used to split. You can also use associate
to allow the option to be specified multiple times, and have its values collected in a map.
ExampleUsage class Build : CliktCommand() {\nval systemProp: Map<String, String> by option(\"-D\", \"--system-prop\").associate()\n\noverride fun run() {\necho(systemProp)\n}\n}\n
$ ./build -Dplace=here --system-prop size=small\n{place=here, size=small}\n
"},{"location":"options/#boolean-flag-options","title":"Boolean Flag Options","text":"Flags are options that don\u2019t take a value. Boolean flags can be enabled or disabled, depending on the name used to invoke the option. You can turn an option into a boolean flag with flag
. That function takes an optional list of secondary names that will be added to any existing or inferred names for the option. If the option is invoked with one of the secondary names, the delegate will return false. It\u2019s a good idea to always set secondary names so that a user can disable the flag if it was enabled previously.
ExampleUsage 1Usage 2 class Cli : CliktCommand() {\nval flag by option(\"--on\", \"-o\").flag(\"--off\", \"-O\", default = false)\noverride fun run() {\necho(flag)\n}\n}\n
$ ./cli -o\ntrue\n
$ ./cli --on --off\nfalse\n
Multiple short flag options can be combined when called on the command line:
ExampleUsage class Cli : CliktCommand() {\nval flagA by option(\"-a\").flag()\nval flagB by option(\"-b\").flag()\nval foo by option(\"-f\")\noverride fun run() {\necho(\"$flagA $flagB $foo\")\n}\n}\n
$ ./cli -abfFoo\ntrue true Foo\n
Tip
You can diasable short option grouping by setting Context.allowGroupedShortOptions
to false
.
"},{"location":"options/#counted-flag-options","title":"Counted Flag Options","text":"You might want a flag option that counts the number of times it occurs on the command line. You can use counted
for this.
ExampleUsage class Log : CliktCommand() {\nval verbosity by option(\"-v\").counted()\noverride fun run() {\necho(\"Verbosity level: $verbosity\")\n}\n}\n
$ ./log -vvv\nVerbosity level: 3\n
"},{"location":"options/#feature-switch-flags","title":"Feature Switch Flags","text":"Another way to use flags is to assign a value to each option name. You can do this with switch
, which takes a map of option names to values. Note that the names in the map replace any previously specified or inferred names.
ExampleUsage class Size : CliktCommand() {\nval size by option().switch(\n\"--large\" to \"large\",\n\"--small\" to \"small\"\n).default(\"unknown\")\noverride fun run() {\necho(\"You picked size $size\")\n}\n}\n
$ ./size --small\nYou picked size small\n
"},{"location":"options/#choice-options","title":"Choice Options","text":"You can restrict the values that a regular option can take to a set of values using choice
. You can also map the input values to new types.
ExampleUsage 1Usage 2Usage 3 class Digest : CliktCommand() {\nval hash by option().choice(\"md5\", \"sha1\")\noverride fun run() {\necho(hash)\n}\n}\n
$ ./digest --hash=md5\nmd5\n
$ ./digest --hash=sha256\nUsage: digest [<options>]\n\nError: Invalid value for \"--hash\": invalid choice: sha256. (choose from md5, sha1)\n
$ ./digest --help\nUsage: digest [<options>]\n\nOptions:\n --hash [md5|sha1]\n -h, --help Show this message and exit\n
"},{"location":"options/#mutually-exclusive-option-groups","title":"Mutually Exclusive Option Groups","text":"If choice
or switch
options aren\u2019t flexible enough, you can use mutuallyExclusiveOptions
to group any nullable options into a mutually exclusive group. If more than one of the options in the group is given on the command line, the last value is used.
If you want different types for each option, you can wrap them in a sealed class.
ExampleUsage 1Usage 2Usage 3 sealed class Fruit {\ndata class Oranges(val size: String): Fruit()\ndata class Apples(val count: Int): Fruit()\n}\nclass Order : CliktCommand() {\nval fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\noption(\"--oranges\").convert { Oranges(it) },\noption(\"--apples\").int().convert { Apples(it) }\n)\n\noverride fun run() = echo(fruit)\n}\n
$ ./order --apples=10\nApples(count=10)\n
$ ./order --oranges=small\nOranges(size=small)\n
$ ./order --apples=10 --oranges=large\nOranges(size=large)\n
You can enforce that only one of the options is given with single
:
ExampleUsage val fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\noption(\"--apples\").convert { Apples(it.toInt()) },\noption(\"--oranges\").convert { Oranges(it) }\n).single()\n
$ ./order --apples=10 --oranges=small\nUsage: order [<options>]\n\nError: option --apples cannot be used with --oranges\n
Like regular options, you can make the entire group required
, or give it a default
value.
Like other option groups, you can specify a name
and help
text for the group if you want to set the group apart in the help output.
"},{"location":"options/#co-occurring-option-groups","title":"Co-Occurring Option Groups","text":"Sometimes you have a set of options that only make sense when specified together. To enforce this, you can make an option group cooccurring
.
Co-occurring groups must have at least one required
option, and may also have non-required options. The required
constraint is enforced if any of the options in the group are given on the command line. If none of the options are given, the value of the group is null.
ExampleUsage 1Usage 2Usage 3 class UserOptions : OptionGroup() {\nval name by option().required()\nval age by option().int()\n}\nclass Tool : CliktCommand() {\nval userOptions by UserOptions().cooccurring()\noverride fun run() {\nuserOptions?.let {\necho(it.name)\necho(it.age)\n} ?: echo(\"No user options\")\n}\n}\n
$ ./tool\nNo user options\n
$ ./tool --name=jane --age=30\njane\n30\n
$ ./tool --age=30\nUsage: tool [<options>]\n\nError: Missing option \"--name\".\n
Like other option groups, you can specify a name
and help
text for the group if you want to set the group apart in the help output.
"},{"location":"options/#choice-and-switch-options-with-groups","title":"Choice and Switch Options With Groups","text":"If you have different groups of options that only make sense when another option has a certain value, you can use groupChoice
and groupSwitch
.
groupChoice
options are similar to choice
options, but instead of mapping a value to a single new type, they map a value to a co-occurring OptionGroup
. Options for groups other than the selected one are ignored, and only the selected group\u2019s required
constraints are enforced. In the same way, groupSwitch
options are similar to switch
options.
ExampleUsage 1Usage 2Usage 3Usage 4 sealed class LoadConfig(name: String): OptionGroup(name)\nclass FromDisk : LoadConfig(\"Options for loading from disk\") {\nval path by option().file().required()\nval followSymlinks by option().flag()\n}\n\nclass FromNetwork: LoadConfig(\"Options for loading from network\") {\nval url by option().required()\nval username by option().prompt()\nval password by option().prompt(hideInput = true)\n}\n\nclass Tool : CliktCommand(help = \"An example of a custom help formatter that uses ansi colors\") {\nval load by option().groupChoice(\n\"disk\" to FromDisk(),\n\"network\" to FromNetwork()\n)\n\noverride fun run() {\nwhen(val it = load) {\nis FromDisk -> echo(\"Loading from disk: ${it.path}\")\nis FromNetwork -> echo(\"Loading from network: ${it.url}\")\nnull -> echo(\"Not loading\")\n}\n}\n}\n
$ ./tool --load=disk --path=./config --follow-symlinks\nLoading from disk: .\\config\n
$ ./tool --load=network --url=www.example.com --username=admin\nPassword: *******\nLoading from network: www.example.com\n
$ ./tool --load=disk\nUsage: cli [<options>]\n\nError: Missing option \"--path\".\n
$ ./tool --load=whoops\nUsage: cli [<options>]\n\nError: Invalid value for \"--load\": invalid choice: whoops. (choose from disk, network)\n
"},{"location":"options/#number-options-without-a-name","title":"Number Options Without a Name","text":"If you have an int or long option, you might want to allow it to be specified without need the option name. For example, git log -2
and git log -n 2
are equivalent. You can add an option like this by passing acceptsValueWithoutName=true
to int()
or long()
.
ExampleUsage 1Usage 2Help Output class Tool : CliktCommand() {\nval level by option(\"-l\", \"--level\", metavar = \"<number>\")\n.int(acceptsValueWithoutName = true)\n\noverride fun run() {\necho(\"Level: $level\")\n}\n}\n
$ ./tool -20\nLevel: 20\n
$ ./tool --level=3\nLevel: 3\n
$ ./tool --help\nUsage: tool [<options>]\n\nOptions:\n-<number>, -l, --level <number>\n-h, --help Show this message and exit\n
Caution
You may only have one option with acceptsValueWithoutName=true
per command
"},{"location":"options/#prompting-for-input","title":"Prompting For Input","text":"In some cases, you might want to create an option that uses the value given on the command line if there is one, but prompt the user for input if one is not provided. Clikt can take care of this for you with the prompt
function.
ExampleUsage 1Usage 2 class Hello : CliktCommand() {\nval name by option().prompt()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
./hello --name=foo\nHello foo\n
./hello\nName: foo\nHello foo\n
The default prompt string is based on the option name, but prompt
takes a number of parameters to customize the output.
"},{"location":"options/#password-prompts","title":"Password Prompts","text":"You can also create an option that uses a hidden prompt and asks for confirmation. This combination of behavior is commonly used for passwords.
ExampleUsage class Login : CliktCommand() {\nval password by option().prompt(requireConfirmation = true, hideInput = true)\noverride fun run() {\necho(\"Your hidden password: $password\")\n}\n}\n
$ ./login\nPassword:\nYour hidden password: hunter2\n
"},{"location":"options/#eager-options","title":"Eager Options","text":"Sometimes you want an option to halt execution immediately and print a message. For example, the built-on --help
option, or the --version
option that many programs have.
The --help
option is added automatically to commands, and --version
can be added using versionOption
. Since the option doesn\u2019t have a value, you can\u2019t define it using a property delegate. Instead, call the function on a command directly, either in an init
block, or on a command instance.
These definitions are equivalent:
Version 1Version 2Usage class Cli : NoOpCliktCommand() {\ninit {\nversionOption(\"1.0\")\n}\n}\nfun main(args: Array<String>) = Cli().main(args)\n
class Cli : NoOpCliktCommand()\nfun main(args: Array<String>) = Cli().versionOption(\"1.0\").main(args)\n
$ ./cli --version\ncli version 1.0\n
"},{"location":"options/#custom-eager-options","title":"Custom Eager Options","text":"If you want to define your own option with a similar behavior, you can do so by calling eagerOption
. This function takes an action
that is called when the option is encountered on the command line. To print a message and halt execution normally from the callback, you can throw a PrintMessage
exception, and CliktCommand.main
will take care of printing the message. If you want to exit normally without printing a message, you can throw Abort(error=false)
instead.
You can define your own version option like this:
class Cli : NoOpCliktCommand() {\ninit {\neagerOption(\"--version\") {\nthrow PrintMessage(\"$commandName version 1.0\")\n}\n}\n}\n
"},{"location":"options/#custom-eager-options-with-values","title":"Custom Eager Options With Values","text":"If you need an eager option that takes a value, pass eager=true
to option()
.
val color by option(eager=true).flag(\"--no-color\", default=true)\n
Warning
Eager options cannot reference other options, since they are finalized first. They can be declared in regular OptionGroups
, but not in other types of groups like [switch groups][#choice-and-switch-options-with-groups].
"},{"location":"options/#deprecating-options","title":"Deprecating Options","text":"You can communicate to users that an option is deprecated with option().deprecated()
. By default, this function will add a tag to the option\u2019s help message, and print a warning to stderr if the option is used.
You can customize or omit the warning message and help tags, or change the warning into an error.
ExampleUsage 1Usage 2Usage 3Usage 4Help Output class Cli : CliktCommand() {\nval opt by option(help = \"option 1\").deprecated()\nval opt2 by option(help = \"option 2\").deprecated(\"WARNING: --opt2 is deprecated, use --new-opt instead\", tagName = null)\nval opt3 by option(help = \"option 3\").deprecated(tagName = \"pending deprecation\", tagValue = \"use --new-opt instead\")\nval opt4 by option(help = \"option 4\").deprecated(error = true)\n\noverride fun run() = echo(\"command run\")\n}\n
$ ./cli --opt=x\nWARNING: option --opt is deprecated\ncommand run\n
$ ./cli --opt2=x\nWARNING: --op2 is deprecated, use --new-opt instead\ncommand run\n
$ ./cli --opt3=x\nWARNING: option --opt3 is deprecated\ncommand run\n
$ ./cli --opt4=x\nERROR: option --opt4 is deprecated\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n --opt <text> option 1 (deprecated)\n --opt2 <text> option 2\n --opt3 <text> option 3 (pending deprecation: use --new-opt instead)\n --opt4 <text> option 4 (deprecated)\n
"},{"location":"options/#unknown-options","title":"Unknown Options","text":"You may want to collect unknown options for manual processing. You can do this by passing treatUnknownOptionsAsArgs = true
to your CliktCommand
constructor. This will cause Clikt to treat unknown options as positional arguments rather than reporting an error when one is encountered. You\u2019ll need to define an argument().multiple()
property to collect the options, otherwise an error will still be reported.
ExampleUsage class Wrapper : CliktCommand(treatUnknownOptionsAsArgs = true) {\ninit { context { allowInterspersedArgs = false } }\n\nval command by option().required()\nval arguments by argument().multiple()\n\noverride fun run() {\nval cmd = (listOf(command) + arguments).joinToString(\" \")\nval proc = Runtime.getRuntime().exec(cmd)\necho(proc.inputStream.bufferedReader().readText())\nproc.waitFor()\n}\n}\n
$ ./wrapper --command=git tag --help | head -n4\nGIT-TAG(1) Git Manual GIT-TAG(1)\n\nNAME\n git-tag - Create, list, delete or verify a tag object signed with GPG\n
Warning
Multiple short options in a single token (e.g. using -abc
to specify -a
, -b
, and -c
in a single token) will still report an error if it contains a mixture of known and unknown options. To avoid this, don\u2019t declare single-letter names for options in commands that use treatUnknownOptionsAsArgs
.
You\u2019ll often want to set allowInterspersedArgs = false
on your Context when using treatUnknownOptionsAsArgs
. You may also find that subcommands are a better fit than treatUnknownOptionsAsArgs
for your use case.
"},{"location":"options/#values-from-environment-variables","title":"Values From Environment Variables","text":"Clikt supports reading option values from environment variables if they aren\u2019t given on the command line. This feature is helpful when automating tools. For example, when using git commit
, you can set the author date with a command line parameter: git commit --date=10/21/2015
. But you can also set it with an environment variable: GIT_AUTHOR_DATE=10/21/2015 git commit
.
Clikt will read option values from environment variables as long as it has an envvar name for the option. There are two ways to set that name: you can set the name manually for an option, or you can enable automatic envvar name inference.
To set the envvar name manually, pass the name to option
:
ExampleUsage 1Usage 2 class Hello : CliktCommand() {\nval name by option(envvar = \"MY_NAME\")\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ export MY_NAME=Foo\n$ ./hello\nHello Foo\n
$ export MY_NAME=Foo\n$ ./hello --name=Bar\nHello Bar\n
You can enable automatic envvar name inference by setting the autoEnvvarPrefix
on a command\u2019s context
. This will cause all options without an explicit envvar name to be given an uppercase underscore-separated envvar name. Since the prefix is set on the context
, it is propagated to subcommands. If you have a subcommand called foo
with an option --bar
, and your prefix is MY_TOOL
, the option\u2019s envvar name will be MY_TOOL_FOO_BAR
.
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext { autoEnvvarPrefix = \"HELLO\" }\n}\nval name by option()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ export HELLO_NAME=Foo\n$ ./hello\nHello Foo\n
"},{"location":"options/#multiple-values-from-environment-variables","title":"Multiple Values from Environment Variables","text":"You might need to allow users to specify multiple values for an option in a single environment variable. You can do this by creating an option with split
.
"},{"location":"options/#flag-option-values-from-environment-variables","title":"Flag Option Values from Environment Variables","text":"For flag options, any of the following (case-insensitive) environment variable values will be interpreted as true
:
\"true\"
, \"t\"
, \"1\"
, \"yes\"
, \"y\"
, \"on\"
The following (case-insensitive) values wil be interpreted as false
:
\"false\"
, \"f\"
, \"0\"
, \"no\"
, \"n\"
, \"off\"
All other values are invalid.
"},{"location":"options/#overriding-system-environment-variables","title":"Overriding system environment variables","text":"You can set a custom function that will be used instead of the system environment variables with ContextBuilder.envvarReader.
@Test\nfun `test envvar`() {\nval envvars = mapOf(\"MY_TOOL_OPTION\" to \"value\")\nval tool = MyTool().context {\nenvvarReader = { envvars[it] }\n}\ntool.parse(emptyList())\nassertEquals(\"value\", tool.option)\n}\n
"},{"location":"options/#values-from-configuration-files","title":"Values from Configuration Files","text":"Clikt also supports reading option values from one or more configuration files (or other sources) when they aren\u2019t present on the command line. For example, when using git commit
, you can set the author email with a command line parameter: git commit --author='Clikt <clikt@example.com>
. But you can also set it in your git configuration file: user.email=clikt@example.com
.
Clikt allows you to specify one or more sources of option values that will be read from with the Context.valueSource
builder.
ExampleUsage class Hello : CliktCommand() {\ninit {\ncontext {\nvalueSource = PropertiesValueSource.from(\"myconfig.properties\")\n}\n}\nval name by option()\noverride fun run() {\necho(\"Hello $name\")\n}\n}\n
$ echo \"name=Foo\" > myconfig.properties\n$ ./hello\nHello Foo\n
You can also pass multiple sources to Context.valueSources
, and each source will be searched for the value in order.
Clikt includes support for reading values from a map, and (on JVM) from Java Properties files. For these two sources, you can customize the keys used to look up options by passing the result of ValueSource.getKey
or ValueSource.envvarKey
to the source\u2019s getKey
constructor parameter.
You can add any other file type by implementing ValueSource. See the JSON sample for an implementation that uses kotlinx.serialization to load values from JSON files.
"},{"location":"options/#configuration-files-and-environment-variables","title":"Configuration Files and Environment Variables","text":"Every option can read values from both environment variables and configuration files. By default, Clikt will use the value from an environment variable before the value from a configuration file, but you can change this by setting Context.readEnvvarBeforeValueSource
to false
.
"},{"location":"options/#windows-and-java-style-option-prefixes","title":"Windows and Java-Style Option Prefixes","text":"When specifying option names manually, you can use any prefix (as long as it\u2019s entirely punctuation).
For example, you can make a Windows-style interface with slashes:
ExampleUsage class Hello: CliktCommand() {\nval name by option(\"/name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello /name Foo\nHello, Foo!\n
Or you can make a Java-style interface that uses single-dashes for long options:
ExampleUsage class Hello: CliktCommand() {\nval name by option(\"-name\", help=\"your name\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
$ ./hello -name Foo\nHello, Foo!\n
Note
Inferred names will always have a POSIX-style prefix like --name
. If you want to use a different prefix, you should specify all option names manually.
"},{"location":"options/#option-transformation-order","title":"Option Transformation Order","text":"Clikt has a large number of extension functions that can modify options. When applying multiple functions to the same option, there\u2019s only one valid order for the functions to be applied. For example, option().default(3).int()
will not compile, because default
must be applied after the value type conversion.
You can call convert
multiple times, but you can only apply one transform of each other type. So option().default(\"\").multiple()
is invalid, since default
and multiple
both transform the call list (if you need a custom default value for multiple
, you can pass it one as an argument).
Here\u2019s an integer option with one of each available transform in a valid order:
val opt: Pair<Int, Int> by option(\"-o\", \"--opt\")\n.int()\n.restrictTo(1..100)\n.pair()\n.default(1 to 2)\n.validate { require(it.second % 2 == 0) }\n
"},{"location":"parameters/","title":"Parameters","text":"Clikt supports two types of parameters: options and positional arguments. If you\u2019re following Unix conventions with your interface, you should use options for most parameters. Options are usually optional, and arguments are frequently required.
"},{"location":"parameters/#differences","title":"Differences","text":"Arguments have the advantage of being able to accept a variable number of values, while Options are limited to a fixed number of values. Other than that restriction, options have more capabilities than arguments.
Options can:
- Act as flags (options don\u2019t have to take values)
- Prompt for missing input
- Load values from environment variables
In general, arguments are usually used for values like file paths or URLs, or for required values, and options are used for everything else.
"},{"location":"parameters/#parameter-names","title":"Parameter Names","text":"Both options and arguments can infer their names (or the metavar in the case of arguments) from the name of the property. You can also specify the names manually. Options can have any number of names, where arguments only have a single metavar.
ExampleHelp Output class Cli : CliktCommand() {\nval inferredOpt by option()\nval inferred by argument()\nval explicitOpt by option(\"-e\", \"--explicit\")\nval explicitArg by argument(\"<explicit>\")\noverride fun run() = Unit\n}\n
Usage: cli [<options>] <inferred> <explicit>\n\nOptions:\n --inferred-opt <text>\n -e, --explicit <text>\n -h, --help Show this message and exit\n
"},{"location":"parameters/#parameter-types","title":"Parameter Types","text":"Both options and arguments can convert the String that the user inputs to other types.
Types work by transforming the return value of the property delegate. By default, parameters have a string type:
val opt: String? by option(help=\"an option\")\nval arg: String by argument(help=\"an argument\")\n
To convert the input to an integer, for example, use the int()
extension function:
val opt: Int? by option(help=\"an option\").int()\nval arg: Int by argument(help=\"an argument\").int()\n
"},{"location":"parameters/#built-in-types","title":"Built-In Types","text":"There are a number of built-in types that can be applied to options and arguments.
"},{"location":"parameters/#int-and-long","title":"Int and Long","text":" option().int()
and argument().int()
option().long()
and argument().long()
option().uint()
and argument().uint()
option().ulong()
and argument().ulong()
By default, any value that fits in the integer type is accepted. You can restrict the values to a range with restrictTo()
, which allows you to either clamp the input to the range, or fail with an error if the input is outside the range.
"},{"location":"parameters/#float-and-double","title":"Float and Double","text":" option().float()
and argument().float()
option().double()
and argument().double()
As with integers, you can restrict the input to a range with restrictTo()
.
"},{"location":"parameters/#boolean","title":"Boolean","text":" option().flag()
option().boolean()
and argument().boolean()
You will normally want to use flags for boolean options. Explicit boolean value conversion is also available if you need, for example, a tri-state Boolean?
parameter.
"},{"location":"parameters/#choice","title":"Choice","text":" option().choice()
and argument().choice()
You can restrict the values to a set of values, and optionally map the input to a new value. For example, to create an option that only accepts the value \u201ca\u201d or \u201cb\u201d:
val opt: String? by option().choice(\"a\", \"b\")\n
You can also convert the restricted set of values to a new type:
val color: Int? by argument().choice(\"red\" to 1, \"green\" to 2)\n
Choice parameters accept values that are case-sensitive by default. This can be configured by passing ignoreCase = true
.
"},{"location":"parameters/#enum","title":"Enum","text":" option().enum()
and argument().enum()
Like choice
, but uses the values of an enum type.
enum class Color { RED, GREEN }\nval color: Color? by option().enum<Color>()\n
Enum parameters accept case-insensitive values by default. This can be configured by passing ignoreCase = false
.
You can also pass a lambda to map the enum values to option names.
val color: Color? by option().enum<Color> { it.name.lowercase() }\n
"},{"location":"parameters/#file-paths","title":"File paths","text":" option().file()
and argument().file()
option().path()
and argument().path()
These conversion functions take extra parameters that allow you to require that values are file paths that have certain attributes, such as that they are directories, or they are writable files.
"},{"location":"parameters/#file-path-inputstream-and-outputstreams","title":"File path InputStream
and OutputStream
s","text":" option().inputStream()
and argument().inputStream()
option().outputStream()
and argument().outputStream()
Like file and path, these conversions take file path values, but expose them as open streams for reading or writing. They support the unix convention of passing -
to specify stdin or stdout rather than a file on the filesystem. You\u2019ll need to close the streams yourself. You can also use stdin or stdout as their default values.
If you need to check if one of these streams is pointing to a file rather than stdin or stdout, you can use isCliktParameterDefaultStdin
or isCliktParameterDefaultStdout
.
"},{"location":"parameters/#custom-types","title":"Custom Types","text":"You can convert parameter values to a custom type by using argument().convert()
and option().convert()
. These functions take a lambda that converts the input String
to any type. If the parameter takes multiple values, or an option appears multiple times in argv
, the conversion lambda is called once for each value.
Any errors that are thrown from the lambda are automatically caught and a usage message is printed to the user. If you need to trigger conversion failure, you can use fail(\"error message\")
instead of raising an exception.
For example, you can create an option of type BigDecimal
like this:
ExampleUsage 1Usage 2 class Cli: CliktCommand() {\nval opt by option().convert { it.toBigDecimal() }\noverride fun run() = echo(\"opt=$opt\")\n}\n
$ ./cli --opt=1.5\nopt=1.5\n
$ ./cli --opt=foo\nUsage: cli [<options>]\n\nError: Invalid value for \"--opt\": For input string: \"foo\"\n
"},{"location":"parameters/#metavars","title":"Metavars","text":"You can also pass option().convert()
a metavar that will be printed in the help page instead of the default of value
. We can modify the above example to use a metavar and an explicit error message:
ExampleUsage 1Usage 2 class Cli: CliktCommand() {\nval opt by option(help=\"a real number\").convert(\"float\") {\nit.toBigDecimalOrNull() ?: fail(\"A real number is required\")\n}\noverride fun run() = echo(\"opt=$opt\")\n}\n
$ ./cli --opt=foo\nUsage: cli [<options>]\n\nError: Invalid value for \"--opt\": A real number is required\n
$ ./cli --help\nUsage: cli [<options>]\n\nOptions:\n --opt <float> a real number\n -h, --help Show this message and exit\n
"},{"location":"parameters/#chaining","title":"Chaining","text":"You can call convert
more than once on the same parameter. This allows you to reuse existing conversion functions. For example, you could automatically read the text of a file parameter.
ExampleUsage class FileReader: CliktCommand() {\nval file: String by argument()\n.file(mustExist=true, canBeDir=false)\n.convert { it.readText() }\noverride fun run() {\necho(\"Your file contents: $file\")\n}\n}\n
$ echo 'some text' > myfile.txt\n$ ./filereader ./myfile.txt\nYour file contents: some text\n
"},{"location":"parameters/#parameter-validation","title":"Parameter Validation","text":"After converting a value to a new type, you can perform additional validation on the converted value with check()
and validate()
(or the argument equivalents).
"},{"location":"parameters/#check","title":"check()
","text":"check()
is similar the stdlib function of the same name: it takes lambda that returns a boolean to indicate if the parameter value is valid or not, and reports an error if it returns false. The lambda is only called if the parameter value is non-null.
ExampleUsage 1Usage 2Usage 3 class Tool : CliktCommand() {\nval number by option(help = \"An even number\").int()\n.check(\"value must be even\") { it % 2 == 0 }\n\noverride fun run() {\necho(\"number=$number\")\n}\n}\n
$ ./tool --number=2\nnumber=2\n
$ ./tool\nnumber=null\n
$ ./tool --number=1\nUsage: tool [<options>]\n\nError: invalid value for --number: value must be even\n
"},{"location":"parameters/#validate","title":"validate()
","text":"For more complex validation, you can use validate()
. This function takes a lambda that returns nothing, but can call fail(\"error message\")
if the value is invalid. You can also call require()
, which will fail if the provided expression is false. Like check
, the lambda is only called if the value is non-null.
The lambdas you pass to validate
are called after the values for all options and arguments have been set, so (unlike in transforms) you can reference other parameters:
ExampleUsage 1Usage 2 class Tool : CliktCommand() {\nval number by option().int().default(0)\nval biggerNumber by option().int().validate {\nrequire(it > number) {\n\"--bigger-number must be bigger than --number\"\n}\n}\n\noverride fun run() {\necho(\"number=$number, biggerNumber=$biggerNumber\")\n}\n}\n
$ ./tool --number=1\nnumber=1, biggerNumber=null\n
$ ./tool --number=1 --bigger-number=0\nUsage: tool [<options>]\n\nError: --bigger-number must be bigger than --number\n
"},{"location":"quickstart/","title":"Quick Start","text":"You can get the library using any maven-compatible build system. Installation instructions can be found in the README.
"},{"location":"quickstart/#basic-concepts","title":"Basic Concepts","text":"Clikt command line interfaces are created by using property delegates inside a CliktCommand
. The normal way to use Clikt is to forward argv
from your main
function to CliktCommand.main
.
The simplest command with no parameters would look like this:
class Hello: CliktCommand() {\noverride fun run() {\necho(\"Hello World!\")\n}\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
And what it looks like to use:
$ ./hello\nHello World!\n
A help page is generated automatically:
$ ./hello --help\nUsage: hello [<options>]\n\nOptions:\n -h, --help Show this message and exit\n
"},{"location":"quickstart/#printing-to-stdout-and-stderr","title":"Printing to Stdout and Stderr","text":"Why does this example use echo
instead of println
? Although println
works, it can cause problems with multi-platform support. echo
uses Mordant to print, so it supports colors and detects the current terminal to make sure that colors work on the current system. You can also pass err=true
to echo
to print to stderr instead of stdout.
Additionally, if you use Clikt\u2019s testing utilities, output sent to echo
will be captured for testing, but output sent to println
will not.
"},{"location":"quickstart/#nesting-commands","title":"Nesting Commands","text":"Instances of any command can be attached to other commands, allowing arbitrary nesting of commands. For example, you could write a script to manage a database:
ExampleUsageHelp Output class Database: CliktCommand(name=\"db\") {\noverride fun run() = Unit\n}\n\nclass Init: CliktCommand(help=\"Initialize the database\") {\noverride fun run() {\necho(\"Initialized the database.\")\n}\n}\n\nclass Drop: CliktCommand(help=\"Drop the database\") {\noverride fun run() {\necho(\"Dropped the database.\")\n}\n}\n\nfun main(args: Array<String>) = Database()\n.subcommands(Init(), Drop())\n.main(args)\n
$ ./db init\nInitialized the database.\n\n$ ./db drop\nDropped the database.\n
$ ./db --help\nUsage: database [<options>] <command> [<args>]...\n\nOptions:\n -h, --help Show this message and exit\n\nCommands:\n init Initialize the database\n drop Drop the database\n
"},{"location":"quickstart/#adding-parameters","title":"Adding Parameters","text":"To add parameters, use the option
and argument
property delegates:
ExampleHelp Output class Hello : CliktCommand() {\nval count by option(help=\"Number of greetings\").int().default(1)\nval name by argument()\n\noverride fun run() {\nfor (i in 1..count) {\necho(\"Hello $name!\")\n}\n}\n}\n
$ ./hello --help\nUsage: hello [<options>] <name>\n\nOptions:\n --count <int> Number of greetings\n -h, --help Show this message and exit\n
"},{"location":"quickstart/#developing-command-line-applications-with-gradle","title":"Developing Command Line Applications With Gradle","text":"When you write a command line application, you probably want to be able to run it without invoking java -jar ...
every time. If you\u2019re using Gradle, the application plugin provides a gradle task that bundles your program jars and scripts to launch them. It makes it easy to build a zip or tarball that you can distribute to your users without them needing to perform any incantations like setting up a classpath. You can see this plugin in use the in Clikt samples.
The application plugin also creates tasks that will build then run your main function directly from within gradle. You can pass command line arguments through to your app with the --args
flag:
$ ./gradlew run --args=\"--count=3 Clikt\"\n
A drawback to using the run
gradle task is that it redirects stdout, so Clikt will not print colors or prompt for input. You can configure the Mordant terminal that Clikt uses to always print with color, but this will cause ANSI codes to be printed even if you redirect the app\u2019s output to a file.
MyCommand().context {\nterminal = Terminal(ansiLevel = AnsiLevel.TRUECOLOR, interactive = true)\n}.main(args)\n
Another approach is to use the installDist
task provided by the plugin. This builds all the distribution scripts in your build folder, which you can then execute normally. See Clikt\u2019s runsample script for an example of this approach.
"},{"location":"testing/","title":"Testing Clikt Commands","text":"Clikt includes the test
extension to help testing commands and their output.
TestCommand @Test\nfun testHello() {\nval command = Hello()\nval result = command.test(\"--name Foo\")\nassertEqual(result.stdout, \"Hello, Foo!\")\nassertEqual(result.exitCode, 0)\nassertEqual(command.name, \"Foo\")\n}\n
class Hello: CliktCommand() {\nval name by option()\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
Calling test
will run the command with the given arguments and return a result object that contains the captured outputs and result status code. You can check the captured output with the stdout
property of the result, errors output with stderr
, or both combined in with output
.
Caution
Output printed with Kotlin\u2019s print
and println
functions are not captured. Use echo
instead.
"},{"location":"testing/#testing-environment-variables","title":"Testing Environment Variables","text":"You can set environment variables for your command by passing in a map of envvars
.
TestCommand @Test\nfun testHello() {\nval command = Hello()\nval result = command.test(\"\", envvars=mapOf(\"HELLO_NAME\" to \"Foo\"))\nassertEqual(result.stdout, \"Hello, Foo!\")\n}\n
class Hello: CliktCommand() {\nval name by option(envvar=\"HELLO_NAME\")\noverride fun run() {\necho(\"Hello, $name!\")\n}\n}\n
To keep tests reproducible, only the envvar values you provide to test
are visible to the command. To include system envvars as well, pass includeSystemEnvvars=true
to test
.
"},{"location":"testing/#testing-prompt-options","title":"Testing Prompt Options","text":"If you use prompt
options, you can use the stdin
parameter of test
to pass a string containing all the lines of input. If you have multiple prompts, each input should be separated by \\n
.
TestCommand @Test\nfun testAdder() {\nval command = Adder()\nval result = command.test(\"\", stdin = \"2\\n3\")\nassertEqual(result.stdout, \"first: second: result: 2 + 3 = 5\")\n}\n
class Adder : TestCommand() {\nval first by option().prompt()\nval second by option().prompt()\n\noverride fun run_() {\necho(\"result: $first + $second = ${first + second}\")\n}\n}\n
"},{"location":"testing/#custom-testing","title":"Custom Testing","text":"If the test
helper doesn\u2019t cover all the use cases you need to test, you can run your command yourself.
In unit tests, you won\u2019t want to use CliktCommand.main
, since it calls exitProcess
when errors occur. Instead, you should instead use CliktCommand.parse
, which throws exceptions with error details rather than printing the details and exiting the process. See the documentation on exceptions for more information on the exceptions that can be thrown.
If your command uses environment variables, you can configure the context to return test values for them.
To capture output, override the command\u2019s console.
"},{"location":"utilities/","title":"Utilities","text":"Writing command line interfaces often involves more than just parsing the command line. Clikt also provides functions to perform actions commonly used in command line programs.
"},{"location":"utilities/#launching-editors","title":"Launching Editors","text":"If you need to ask users for multi-line input, or need to have the user edit a file, you can do so through editText
and editFile
. These functions open the program defined in the VISUAL
or EDITOR
environment variables, or a sensible default if neither are defined. The functions return the edited text if the user saved their changes.
Example fun getCommitMessage(): String? {\nval message = \"\"\"\n # Enter your message.\n # Lines starting with # are ignored\n \"\"\".trimIndent()\nreturn editText(message, requireSave = true)\n?.replace(Regex(\"#[^\\n]*\\n\"), \"\")\n}\n
"},{"location":"utilities/#input-prompts","title":"Input Prompts","text":"Options can prompt for values automatically, but you can also do so manually by using Mordant\u2019s prompt functionality directly. By default, it accepts any input string, but you can also pass in a conversion function. If the conversion returns a ConversionResult.Invalid
, the prompt will ask the user to enter a different value.
ExampleInteractive Session val input = terminal.prompt(\"Enter a number\") {\nit.toIntOrNull()\n?.let { ConversionResult.Valid(it) }\n?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\necho(\"Twice your number is ${input * 2}\")\n
Enter a number: foo\nError: foo is not a valid integer\nEnter a number: 11\nTwice your number is 22\n
"},{"location":"utilities/#confirmation-prompts","title":"Confirmation Prompts","text":"You can also ask the user for a yes or no response with Mordant\u2019s YesNoPrompt
:
if (YesNoPrompt(\"Continue?\", terminal).ask() == true) {\necho(\"Ok!\")\n}\n
"},{"location":"whyclikt/","title":"Why Clikt?","text":"There are existing Kotlin libraries for creating command line interfaces, and many Java libraries work in Kotlin as well. However, none of them had all the following features:
- Unrestricted composability of commands
- Fully static type safety for parameters
- Composable parameter customization that doesn\u2019t require registering converter objects.
- Full support for Unix command line conventions
- Capable of reading parameter values from environment variables out of the box
- Included support for common functionality (keyboard interactivity, line ending normalization, launching editors, etc.)
- Built-in support for multi-token command aliases
Clikt is focused on making writing robust, posix-compliant command line interfaces as easy as possible. A good CLI does more than just parse argv
. It allows users to specify values in environment variables, and in some cases prompts for additional input, or opens an editor. Clikt supports all of this out of the box.
Sometimes you need to make a CLI that doesn\u2019t follow Unix conventions. You might be writing for windows, or you want to use the Java style of long options with a single dash. Maybe you need to use a bunch of required options instead of arguments, or you want the help page formatted differently. \u201cBest practices\u201d might not be the best for you, so Clikt tries to make implementing uncommon use-cases as easy as possible.
"},{"location":"whyclikt/#why-not-a-kotlin-library-like-kotlin-argparser-or-kotlinxcli","title":"Why not a Kotlin library like kotlin-argparser or kotlinx.cli?","text":"Clikt isn\u2019t the only Kotlin CLI library. kotlin-argparser and kotlinx.cli both predate Clikt\u2019s creation.
Both, like Clikt, use property delegates to define parameters, but they\u2019re missing most of Clikt features and its extensible design.
kotlinx.cli was written by JetBrains and mostly copied kotlin-argparser\u2018s design (and, later, some of Clikt\u2019s).
kotlin-argparser works well for simple cases. It\u2019s missing a lot of features that Clikt has, but features could be added. Its real drawback is that it fundamentally does not support composition of commands or parameter values. The lack of subcommand support was already a non-starter, but there are other design decisions that make it unsuitable.
In the simple cases, the two libraries are similar. Here\u2019s an example from its README:
class MyArgs(parser: ArgParser) {\nval v: Boolean by parser.flagging(help=\"enable verbose mode\")\nval username: String? by parser.storing(help=\"name of the user\")\nval count: Int? by parser.storing(help=\"number of the widgets\") { toInt() }\nval source: List<String> by parser.positionalList(help=\"source filenames\")\nval destination: String by parser.positional(help=\"destination\")\n}\n\nfun main(args: Array<String>) = mainBody {\nArgParser(args).parseInto(::MyArgs).run {\nprintln(\"Hello, $username!\")\nprintln(\"Moving $count widgets from $source to $destination.\")\n}\n}\n
Here\u2019s the same thing with Clikt:
class Cli : CliktCommand() {\nval v: Boolean by option(help = \"enable verbose mode\").flag()\nval username: String? by option(help = \"name of the user\")\nval count: Int? by option(help = \"number of the widgets\").int()\nval source: List<String> by argument(help = \"source filenames\").multiple()\nval destination: String by argument(help = \"destination\")\noverride fun run() {\nprintln(\"Hello, $name!\")\nprintln(\"Moving $count widgets from $source to $destination.\")\n}\n}\n\nfun main(args: Array<String>) = Cli().main(args)\n
Both work fine, although you may find Clikt more consistent and a bit less verbose. The differences become more pronounced once you try to do anything that isn\u2019t built in to kotlin-argparser.
Maybe you need an option to take two values. Here\u2019s another example from the kotlin-argparser
README showing how to do that:
fun ArgParser.putting(vararg names: String, help: String) =\noption<MutableMap<String, String>>(*names,\nargNames = listOf(\"KEY\", \"VALUE\"),\nhelp = help) {\nvalue.orElse { mutableMapOf<String, String>() }.apply {\nput(arguments.first(), arguments.last()) }\n}\n\nfun ArgParser.putting(help: String) =\nArgParser.DelegateProvider { identifier ->\nputting(identifierToOptionName(identifier), help = help) }\n\nclass MyArgs(parser: ArgParser) {\nval v by parser.putting(help=\"this takes two values\")\n}\n
Clikt has that functionality built in as option().pair()
, but you could implement it yourself like this:
class Cli : CliktCommand() {\nval v by option(help=\"this takes two values\").transformValues(2) { it[0] to it[1] }\n}\n
The Clikt version is of course much simpler, but there are more fundamental issues with the kotlin-argparser
version that drove the creation of Clikt:
- Its inheritance-based design means that if you wanted to change the type of each value, you would have to copy all the code for each type. With Clikt, you could just do
option().int().transformValues(2) { it[0] to it[1] }
- Its inheritance-based design means that supporting types, multiple values, and multiple option occurrences would require a combinatorial number of copies of the above code. With Clikt, these are all orthogonal.
- You have to do all error checking yourself. The
argparser
example silently discards extra values, or copies the single value, rather than inform the user of the mistake. You could write more code to do so, but Clikt takes care of it for you. - Option name inference is not automatic, requiring you to wrap the delegate with yet another function.
- Each delegate function has a different name, with no indication of whether it\u2019s creating an option or positional argument. With Clikt, all options are created with
option()
, and all arguments with argument()
.
Some of these problems can be solved by writing more code, and some can\u2019t. On the other hand, Clikt attempts to have a consistent, intuitive, composable design that does the right thing without forcing you to think about edge cases.
"},{"location":"whyclikt/#why-not-a-java-library-like-jcommander-or-picocli","title":"Why not a Java library like JCommander or Picocli?","text":"There are a lot of command line libraries for Java. Most are verbose and not composable. Two popular Java libraries that are usable from Kotlin are JCommander and picocli.
These libraries use annotations to define parameters, and reflection to set fields. This is functional for simple types, but defining your own types requires you to register a type adapter with the library. This means that type errors are not caught until runtime, and many types of customization are not possible.
For example, in JCommander, options that take multiple values cannot be converted to other types. The JCommander docs explain:
\u2026 only List is allowed for parameters that define an arity. You will have to convert these values yourself if the parameters you need are of type Integer or other (this limitation is due to Java\u2019s erasure).
You also can\u2019t customize many aspects of parsing in JCommander. It can\u2019t infer parameter names. With JCommander, you can\u2019t have an option with multiple values and multiple occurrences at the same time. You can\u2019t have more than one argument, and it can only take one value or an unlimited number of values. You can\u2019t nest subcommands.
JCommander and piocli are great libraries if you\u2019re writing code in Java, but we can do much better with Kotlin.
"}]}
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 055ca91b..1a7db15e 100755
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ
diff --git a/testing/index.html b/testing/index.html
index cae87c6d..9f17c7db 100755
--- a/testing/index.html
+++ b/testing/index.html
@@ -2083,7 +2083,7 @@ Custom Testing
- Copyright © 2022 AJ Alt
+ Copyright © 2018 AJ Alt
diff --git a/utilities/index.html b/utilities/index.html
index 845e18fd..60cd56c6 100755
--- a/utilities/index.html
+++ b/utilities/index.html
@@ -2102,7 +2102,7 @@ Confirmation Prompts
- Copyright © 2022 AJ Alt
+ Copyright © 2018 AJ Alt
diff --git a/whyclikt/index.html b/whyclikt/index.html
index 7c7ea537..0739eeb0 100755
--- a/whyclikt/index.html
+++ b/whyclikt/index.html
@@ -2166,7 +2166,7 @@ Why not a Java librar
- Copyright © 2022 AJ Alt
+ Copyright © 2018 AJ Alt