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() {\n val count: Int by option().int().default(1).help(\"Number of greetings\")\n val name: String by option().prompt(\"Your name\").help(\"The person to greet\")\n\n override fun run() {\n repeat(count) {\n echo(\"Hello $name!\")\n }\n }\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
dependencies {\n implementation(\"com.github.ajalt.clikt:clikt:5.0.0\")\n\n // optional support for rendering markdown in help messages\n implementation(\"com.github.ajalt.clikt:clikt-markdown:5.0.0\")\n}\n
There is also a smaller core module available. See the docs for details.
"},{"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 most multiplatform targets. 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:
"},{"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() {\n private val token by option(help=\"api token to use for requests\").default(\"...\")\n private val hostname by option(help=\"base url for requests\").default(\"example.com\")\n\n override fun run() {\n currentContext.obj = Config(token, hostname)\n }\n}\n\nclass Store : CliktCommand() {\n private val file by option(help=\"file to store\").file(canBeDir = false)\n private val config by requireObject<Config>()\n override fun run() {\n myApiStoreFile(config.token, config.hostname, file)\n }\n}\n\nclass Fetch : CliktCommand() {\n private val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\n private val config by requireObject<Config>()\n override fun run() {\n myApiFetchFile(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
"},{"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:\") {\n val token by option(help=\"api token to use for requests\").default(\"...\")\n val hostname by option(help=\"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : CliktCommand() {\n private val commonOptions by CommonOptions()\n private val file by option(help=\"file to store\").file(canBeDir = false)\n override fun run() {\n myApiStoreFile(commonOptions.token, commonOptions.hostname, file)\n }\n}\n\nclass Fetch : CliktCommand() {\n private val commonOptions by CommonOptions()\n private val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\n override fun run() {\n myApiFetchFile(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
"},{"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() {\n val token by option(help = \"api token to use for requests\").default(\"...\")\n val hostname by option(help = \"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : MyApiSubcommand() {\n private val file by option(help = \"file to store\").file(canBeDir = false)\n override fun run() {\n myApiStoreFile(token, hostname, file)\n }\n}\n\nclass Fetch : MyApiSubcommand() {\n private val outdir by option(help = \"directory to store file in\").file(canBeFile = false)\n override fun run() {\n myApiFetchFile(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
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.\n override fun aliases(): Map<String, List<String>> = mapOf(\n \"ci\" to listOf(\"commit\"),\n \"cm\" to listOf(\"commit\", \"-m\")\n )\n}\n\nclass Commit: CliktCommand() {\n val message by option(\"-m\").default(\"\")\n override fun run() {\n echo(\"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() {\n override fun aliases(): Map<String, List<String>> {\n val prefixCounts = mutableMapOf<String, Int>().withDefault { 0 }\n val prefixes = mutableMapOf<String, List<String>>()\n for (name in registeredSubcommandNames()) {\n if (name.length < 3) continue\n for (i in 1..name.lastIndex) {\n val prefix = name.substring(0..i)\n prefixCounts[prefix] = prefixCounts.getValue(prefix) + 1\n prefixes[prefix] = listOf(name)\n }\n }\n return prefixes.filterKeys { prefixCounts.getValue(it) == 1 }\n }\n}\n\nclass Foo: CliktCommand() {\n override fun run() {\n echo(\"Running Foo\")\n }\n}\n\nclass Bar: CliktCommand() {\n override fun run() {\n echo(\"Running Bar\")\n }\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Foo(), Bar()).main(args)\n
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 transformToken on the command\u2019s context, which will be called for each option and command name that\u2019s input. This can be used to implement case-insensitive parsing, for example:
ExampleUsage
class Hello : CliktCommand() {\n init {\n context { transformToken = { it.lowercase() } }\n }\n\n val name by option()\n override 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.
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:
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.
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.
You might need to open a resource like a file or a network connection in one command, and use it in its subcommands.
The typical way to manage a resource is with the use function:
class MyCommand : CliktCommand() {\n private val file by option().file().required()\n\n override fun run() {\n file.bufferedReader().use { reader ->\n // use the reader\n }\n }\n}\n
But if you need to share the resource with subcommands, the use function will exit and close the resource before the subcommand is called. Instead, use the context\u2019s registerCloseable function (for kotlin.AutoCloseable) or registerJvmCloseable function (for java.lang.AutoCloseable) to:
class MyCommand : CliktCommand() {\n private val file by option().file().required()\n\n override fun run() {\n currentContext.obj = currentContext.registerJvmCloseable(file.bufferedReader())\n }\n}\n
You can register as many closeables as you need, and they will all be closed when the command and its subcommands have finished running. If you need to manage a resource that isn\u2019t AutoClosable, you can use callOnClose.
"},{"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/#custom-run-function-signature","title":"Custom run function signature","text":"
Clikt provides a few command base classes that have different run function signatures. CliktCommand has fun run(), while SuspendingCliktCommand has suspend fun run(). If you want a run function with a different signature, you can define your own base class the inherits from BaseCliktCommand and use the CommandLineParser methods to parse and run the command.
For example, if you want a command that uses a Flow to emit multiple value for each run, you could implement it like this:
ExampleUsage
abstract class FlowCliktCommand : BaseCliktCommand<FlowCliktCommand>() {\n abstract fun run(): Flow<String>\n}\n\nclass MyFlowCommand : FlowCliktCommand() {\n val opt by option().required()\n val arg by argument().int()\n\n override fun run(): Flow<String> = flow {\n emit(opt)\n emit(arg.toString())\n }\n}\n\nclass MyFlowSubcommand : FlowCliktCommand() {\n val arg by argument().multiple()\n\n override fun run(): Flow<String> = flow {\n arg.forEach { emit(it) }\n }\n}\n\nfun FlowCliktCommand.parse(argv: Array<String>): Flow<String> {\n val flows = mutableListOf<Flow<String>>()\n CommandLineParser.parseAndRun(this, argv.asList()) { flows += it.run() }\n return flow { flows.forEach { emitAll(it) } }\n}\n\nfun FlowCliktCommand.main(argv: Array<String>): Flow<String> {\n return CommandLineParser.mainReturningValue(this) { parse(argv) }\n}\n\nsuspend fun main(args: Array<String>) {\n val command = MyFlowCommand().subcommands(MyFlowSubcommand())\n val resultFlow: Flow<String> = command.main(args)\n resultFlow.collect {\n command.echo(it)\n }\n}\n
$ ./command --opt=foo 11 my-flow-subcommand bar baz\nfoo\n11\nbar\nbaz\n
There are a number of steps here, so let\u2019s break it down:
Define a base class FlowCliktCommand that inherits from BaseCliktCommand and has an abstract run function that returns a Flow.
Define your commands that inherit from FlowCliktCommand and implement your run function.
Define an extension function parse that uses CommandLineParser.parseAndRun to parse the command and run the run function.
Define an extension function main that uses CommandLineParser.main to run the parse function and handle any exceptions it might throw.
In your main function, call main on your command, and collect the results of the Flow.
If you want to customize the behavior even further, see the next section.
"},{"location":"advanced/#custom-run-behavior","title":"Custom run behavior","text":"
If you want to customize how or when subcommands are run, you can do so by defining a custom base class like in the previous section, but instead of using CommandLineParser.parseAndRun, you can call your command\u2019s run functions manually.
For example, if you want commands to return status codes, and you want to stop running commands as soon as one of them returns a non-zero status code, you could implement it like this:
ExampleUsage
abstract class StatusCliktCommand : BaseCliktCommand<StatusCliktCommand>() {\n abstract fun run(): Int\n}\n\nclass ParentCommand : StatusCliktCommand() {\n override val allowMultipleSubcommands: Boolean = true\n override fun run(): Int {\n echo(\"Parent\")\n return 0\n }\n}\n\nclass SuccessCommand : StatusCliktCommand() {\n override fun run(): Int {\n echo(\"Success\")\n return 0\n }\n}\n\nclass FailureCommand : StatusCliktCommand() {\n override fun run(): Int {\n echo(\"Failure\")\n return 1001\n }\n}\n\nfun StatusCliktCommand.parse(argv: Array<String>): Int {\n val parseResult = CommandLineParser.parse(this, argv.asList())\n parseResult.invocation.flatten().use { invocations ->\n for (invocation in invocations) {\n val status = invocation.command.run()\n if (status != 0) {\n return status\n }\n }\n }\n return 0\n}\n\nfun StatusCliktCommand.main(argv: Array<String>) {\n val status = CommandLineParser.mainReturningValue(this) { parse(argv) }\n exitProcess(status)\n}\n\nfun main(argv: Array<String>) {\n ParentCommand().subcommands(SuccessCommand(), FailureCommand()).main(argv)\n}\n
The steps here are similar to the previous section, but instead of using CommandLineParser.parseAndRun, we use CommandLineParser.parse, then call run on each command invocation manually, and stop when one of them returns a non-zero status code.
Clikt normally uses Mordant for rendering output and interacting with the system, but there are some cases where you might want to use Clikt without Mordant. For these cases, Clikt has a core module that doesn\u2019t have any dependencies.
Replace your Clikt dependency with the core module:
The CliktCommand class is only available in the full module, so you\u2019ll need to use CoreCliktCommand (or CoreNoOpCliktCommand) instead. The CoreCliktCommand has the same API as CliktCommand, but it doesn\u2019t have any of these features built in:
Text wrapping, formatting, markdown, or color support
argument files
environment variables
main exiting the process with a status code
printing to stderr
prompt options
The test function
Most of those features can be added by setting the appropriate properties on the command\u2019s context. Here\u2019s an example of setting all of them using Java APIs, but you only need to set the ones you\u2019ll use:
There are some JVM-only extensions available such as file and path parameter types.
"},{"location":"advanced/#desktop-native-linux-windows-and-macos","title":"Desktop native (Linux, Windows, and macOS)","text":""},{"location":"advanced/#javascript-and-wasmjs","title":"JavaScript and WasmJS","text":"
All functionality is supported on Node.js.
In the browser, 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.
"},{"location":"advanced/#ios","title":"iOS","text":""},{"location":"advanced/#watchos-tvos-and-wasmwasi","title":"watchOS, tvOS and WasmWasi","text":"
These platforms are not supported by the markdown module, but all other functionality is available.
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.
By default, argument takes a single String value which is required to be provided on the command line.
ExampleUsage
class Hello : CliktCommand() {\n val name by argument()\n override fun run() {\n echo(\"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(\n help = \"Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\"\n) {\n private val source by argument().file(mustExist = true).multiple()\n private val dest by argument().file()\n override 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
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() {\n val source: List<Path> by argument().path(mustExist = true).multiple()\n val dest: Path by argument().path(canBeFile = false)\n override fun run() {\n echo(\"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
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() {\n val verbose by option().flag()\n val files by argument().multiple()\n override fun run() {\n if (verbose) echo(files.joinToString(\"\\n\"))\n }\n}\n
$ ./touch --foo.txt\nUsage: touch [<options>] [<files>]...\n\nError: no such option: \"--foo.txt\".\n
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 by overriding the autoCompleteEnvvar property in your command. 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:
"},{"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() {\n init {\n completionOption()\n }\n // ...\n}\n
class MyCommand: CliktCommand() {\n //..\n}\n\nfun main(args: Array<String>) = MyCommand().completionOption().main(args)\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.
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.
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.\n val name by option(completionCandidates =\n CompletionCandidates.Custom.fromStdout(\"echo completion1 completion2\")\n )\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
class Hello: CliktCommand() {\n // This is identical to the previous example\n val name by option(completionCandidates = CompletionCandidates.Custom {\n \"\"\"\n WORDS=${'$'}(echo completion1 completion2)\n COMPREPLY=(${'$'}(compgen -W \"${'$'}WORDS\" -- \"${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}\"))\n \"\"\".trimIndent()\n })\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
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.
Added NoSuchArgument exception that is thrown when too many arguments were given on the command line. Previously, a less specific UsageError was thrown instead.
Added CommandLineParser.tokenize that splits a string into argv tokens.
Added CommandLineParser that provides functions for parsing and finalizing commands manually for more control.
Added Context.invokedSubcommands that contains all subcommands of the current command that are going to be invoked when allowMultipleSubcommands is true.
Added SuspendingCliktCommand that has a suspend fun run method, allowing you to use coroutines in your commands.
Added ChainedCliktCommand that allows you to return a value from your run method and pass it to the next command in the chain.
Added Context.data as an alternative to obj that allows you to store more than one object in the context.
Added Context.echoer to customize how echo messages are printed.
Added CompletionGenerator to manually generate completions for a command.
Added Context.exitProcess which you can use to prevent the process from exiting during tests.
Added core module that supports watchOS, tvOS, and wasmWasi targets and has no dependencies.
Added more options to CliktCommand.test to control the terminal interactivity. (#517)
Added associate{}, associateBy{}, and associateWith{} transforms for options that allow you to convert the keys and values of the map. (#529)
Added support for aliasing options to other options. (#535)
Added limit and ignoreCase parameters to option().split(). (#541)
Support calling --help on subcommands when parents have required parameters.
In a subcommand with and an argument() with multiple() or optional(), the behavior is now the same regardless of the value of allowMultipleSubcommands: if a token matches a subcommand name, it\u2019s now treated as a subcommand rather than a positional argument.
Due to changes to the internal parsing algorithm, the exact details of error messages when multiple usage errors occur have changed in some cases.
Breaking Change: Moved the following parameters from CliktCommand\u2019s constructor; override the corresponding properties instead:
removed parameter replacement property helpfun helpepilogfun helpEpiloginvokeWithoutSubcommandval invokeWithoutSubcommandprintHelpOnEmptyArgsval printHelpOnEmptyArgshelpTagsval helpTagsautoCompleteEnvvarval autoCompleteEnvvarallowMultipleSubcommandsval allowMultipleSubcommandstreatUnknownOptionsAsArgsval treatUnknownOptionsAsArgshiddenval hiddenFromHelp - The following methods on CliktCommand have been renamed: commandHelp -> help, commandHelpEpilog -> epilog. The old names are deprecated. - Breaking Change: CliktCommand.main and CliktCommand.parse are now extension functions rather than methods. - Breaking Change: Context.obj and Context.terminal, and OptionTransformContext.terminal are now extension functions rather than properties. - Breaking Change: The RenderedSection and DefinitionRow classes have moved to AbstractHelpFormatter. - Markdown support in the help formatter is no longer included by default. To enable it, include the :clikt-markdown dependency and call yourCommand.installMordantMarkdown() before parsing. - Updated Kotlin to 2.0.0"},{"location":"changelog/#fixed","title":"Fixed","text":"
Fixed excess arguments not being reported when allowMultipleSubcommands=true and a subcommand has excess arguments followed by another subcommand.
Commands with printHelpOnEmptyArgs=true will no longer print help if an option has a value from an environment variable or value source. (#382)
Deprecated Context.originalArgv. It will now always return an empty list. If your commands need an argv, you can pass it to them before you run them, or set in on the new Context.data map.
Deprecated Context.expandArgumentFiles. Use Context.argumentFileReader instead.
Renamed the following Context fields to be more consistent. The old names are deprecated.
old name new name Context.envvarReaderContext.readEnvvarContext.correctionSuggestorContext.suggestTypoCorrectionContext.argumentFileReaderContext.readArgumentFileContext.tokenTransformerContext.transformToken"},{"location":"changelog/#removed","title":"Removed","text":"
Added limit parameter to option().counted() to limit the number of times the option can be used. You can either clamp the value to the limit, or throw an error if the limit is exceeded. (#483)
Added Context.registerClosable and Context.callOnClose to allow you to register cleanup actions that will be called when the command exits. (#395)
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)
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)
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.
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.
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>.
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
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.
InputStream.isCliktParameterDefaultStdin and OutputStream.isCliktParameterDefaultStdout to check if the streams returned from inputStream/outputStream options are proxying stdin/stdout (#272)
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.
@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)
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)
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.
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.
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)
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)
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
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()
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.
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)
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)
CompletionCandidates.Fixed now has a secondary convenience constructor that take a vararg of Strings
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.
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)
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.
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
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.
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.
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() {\n val verbose by option().flag(\"--no-verbose\")\n override fun run() {\n echo(\"Verbose mode is ${if (verbose) \"on\" else \"off\"}\")\n }\n}\n\nclass Execute : CliktCommand() {\n override fun run() {\n echo(\"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
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() {\n override fun run()= Unit\n}\n\nclass Execute : CliktCommand(name = \"RUN-ME\") {\n override fun run() {\n echo(\"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
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() {\n override fun help(context: Context) = \"A tool that runs\"\n val verbose by option().flag(\"--no-verbose\")\n override fun run() = Unit\n}\n\nclass Execute : CliktCommand() {\n override fun help(context: Context) = \"Execute the command\"\n val name by option()\n override 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\u2019s 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 a [data][Context.data] map and obj property that hold objects that can be accessed from child commands.
ExampleUsage
data class MyConfig(var verbose: Boolean = false)\n\nclass Tool : CliktCommand() {\n val verbose by option().flag(\"--no-verbose\")\n val config by findOrSetObject { MyConfig() }\n override fun run() {\n config.verbose = if (verbose) \"on\" else \"off\"\n }\n}\n\nclass Execute : CliktCommand() {\n val config by requireObject<MyConfig>()\n override fun run() {\n echo(\"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. If you need more than one object, you can pass a key to these functions, and they\u2019ll look for an object with that key and type in the context\u2019s data map.
Keep in mind 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() {\n override fun run() {\n // runs eagerly\n currentContext.findOrSetObject { MyConfig() }\n }\n}\n
class Tool : CliktCommand() {\n override fun run() {\n // runs eagerly, won't look for parent contexts\n currentContext.obj = MyConfig()\n }\n}\n
If you need to share resources that need to be cleaned up, you can use currentContext.registerCloseable
"},{"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() {\n override val invokeWithoutSubcommand = true\n override fun run() {\n val subcommand = currentContext.invokedSubcommand\n if (subcommand == null) {\n echo(\"invoked without a subcommand\")\n } else {\n echo(\"about to run ${subcommand.name}\")\n }\n }\n}\n\nclass Execute : CliktCommand() {\n override fun run() {\n echo(\"running subcommand\")\n }\n}\n
$ ./tool\ninvoked without a subcommand\n
$./tool execute\nabout to run execute\nrunning subcommand\n
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:
$ ./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 overriding printHelpOnEmptyArgs = true in your command. 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() {\n override val printHelpOnEmptyArgs = true\n val arg by argument()\n override 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\n val opt by option().validate {\n if (it.isEmpty()) message(\"Empty strings are not recommended\")\n }\n override fun run() {\n echo(\"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() {\n init { context { printExtraMessages = false } }\n val opt by option().validate {\n if (it.isEmpty()) message(\"Empty strings are not recommended\")\n }\n override fun run() {\n echo(\"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, override allowMultipleSubcommands = true in your command.
ExampleUsage
class Compiler: CliktCommand() {\n override val allowMultipleSubcommands = true\n override fun run() {\n echo(\"Running compiler\")\n }\n}\n\nclass Clean: CliktCommand() {\n val force by option().flag()\n override fun run() {\n echo(\"Cleaning (force=$force)\")\n }\n}\n\nclass Build: CliktCommand() {\n val file by argument().file()\n override fun run() {\n echo(\"Building $file\")\n }\n}\n\nfun main(args: Array<String>) = Compiler().subcommands(Clean(), Build()).main(args)\n
The parent command will run once, and each subcommand will run once each time they\u2019re called.
Warning
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.
Subcommands of a command with allowMultipleSubcommands=true can themselves have subcommands, but cannot have allowMultipleSubcommands=true.
If you have multiple subcommands you might want to pass the output of one subcommand to the next. For example, the ImageMagick tool lets you apply a series of transformations to an image by invoking multiple subcommands.
To do this with Clikt, you could pass your output through the Context.obj, but another option is to use a ChainedCliktCommand, which allows you to return values from your run function that will be passed to the next subcommand.
In this example, we\u2019ll write simple text editing pipeline that takes an initial string, and then applies a series of transformations to it, printing the final result:
ExampleUsage
class EditText : ChainedCliktCommand<String>() {\n override val allowMultipleSubcommands: Boolean = true\n val text by argument()\n override fun run(value: String): String = text\n}\n\nclass RepeatText : ChainedCliktCommand<String>(\"repeat\") {\n val count by option().int().default(1)\n override fun run(value: String): String {\n return value.repeat(count)\n }\n}\n\nclass UppercaseText : ChainedCliktCommand<String>(\"uppercase\") {\n override fun run(value: String): String {\n return value.uppercase()\n }\n}\n\nclass ReplaceText : ChainedCliktCommand<String>(\"replace\") {\n val oldValue by argument()\n val newValue by argument()\n override fun run(value: String): String {\n return value.replace(oldValue, newValue)\n }\n}\n\nfun main(args: Array<String>) {\n val command = EditText()\n .subcommands(RepeatText(), UppercaseText(), ReplaceText())\n val result = command.main(args, \"\")\n command.echo(result)\n}\n
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.
You can add help text to commands and parameters. For parameters, you can pass a help string or use the help() extension. For commands, you can override the help and helpEpilog methods.
ExampleAlternate styleHelp output
class Hello : CliktCommand() {\n override fun help(context: Context) = \"\"\"\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n \"\"\".trimIndent()\n val count by option(\"-c\", \"--count\", metavar=\"count\", help=\"number of greetings\")\n .int().default(1)\n val name by argument(help=\"The name to greet\")\n override fun run() = repeat(count) { echo(\"Hello $commandName!\") }\n}\n
class Hello : CliktCommand() {\n override fun help(context: Context): String {\n val style = context.theme.info\n return \"\"\"\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\n val count by option(\"-c\", \"--count\", metavar=\"count\").int().default(1)\n .help { theme.success(\"number of greetings\") }\n val name by argument()\n .help(\"The name to greet\")\n override 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":"
You can configure Clikt to use Mordant to render Markdown in help texts. You can use all the normal markdown features, such as lists, tables, and even hyperlinks if your terminal supports them.
First, add the :clitk-markdown dependency to your project:
And install the markdown help formatter on your command:
val command = MyCommand().installMordantMarkdown()\n
Then you can use markdown in your help strings:
ExampleHelp output
class Tool : NoOpCliktCommand() {\n init {\n installMordantMarkdown()\n }\n val 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() {\n val 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() {\n override fun help(context: Context) = \"\"\"\n Execute the command.\n\n The command will be executed.\n \"\"\".trimIndent()\n}\n\nclass Abort : NoOpCliktCommand() {\n override fun help(context: Context) = \"Kill any running commands.\"\n}\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
Clikt adds a help option to every command automatically. It uses the names -h and --help and prints the command\u2019s help message when invoked.
"},{"location":"documenting/#changing-the-help-option-names","title":"Changing the help option names","text":"
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 {\n override fun helpOptionMessage(): String = \"show the help\"\n}\n\nclass Tool : NoOpCliktCommand() {\n init {\n context {\n helpOptionNames = setOf(\"/help\")\n localization = 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/#changing-the-help-option-behavior","title":"Changing the help option behavior","text":"
If you want to run some code when the help option is invoked, or change its behavior, you can define the option yourself. The default help option is an eager option that throws a PrintHelpMessage, so if you wanted to log some information when the help option is invoked, you could do something like this:
ExampleExample 2Usage
class CustomHelpCommand : TestCommand() {\n init {\n eagerOption(\"-h\", \"--help\", help=\"Show this message and exit\") {\n echo(\"about to print help\")\n throw PrintHelpMessage(context)\n }\n }\n}\n
// If you want to use the help message from the localization, you can register an option\n// with eager=true and use the lazy `help` method.\nclass CustomHelpCommand : TestCommand() {\n init {\n registerOption(\n option(\"-h\", \"--help\", eager=true).flag()\n .help { context.localization.helpOptionMessage() }\n .validate {\n if(it) {\n echo(\"about to print help\")\n throw PrintHelpMessage(context)\n }\n }\n )\n }\n}\n
$ ./tool --help\nabout to print help\nUsage: custom-help [<options>]\n\nOptions:\n -h, --help Show this message and exit\n
Warning
Eager options can\u2019t reference other options or arguments, since they\u2019re evaluated before parsing the rest of the command line.
"},{"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() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, showDefaultValues = true) }\n }\n }\n\n val a by option(help = \"this is optional\").default(\"value\")\n val 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.
You can pass a character to the requiredOptionMarker argument of the MordantHelpFormatter.
ExampleUsage
class Tool : NoOpCliktCommand() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, requiredOptionMarker = \"*\") }\n }\n }\n\n val option by option(help = \"this is optional\")\n val 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
You can also show a tag for required options by passing showRequiredTag = true to the MordantHelpFormatter.
ExampleUsage
class Tool : CliktCommand() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, showRequiredTag = true) }\n }\n }\n\n val option by option(help = \"this is optional\")\n val 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(\n name = \"User Options\",\n help = \"Options controlling the user\"\n) {\n val name by option(help = \"user name\")\n val age by option(help = \"user age\").int()\n}\n\nclass Tool : NoOpCliktCommand() {\n val 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 suggestTypoCorrection on your command\u2019s context.
class Cli : NoOpCliktCommand() {\n init {\n context {\n // Only suggest corrections that start with the entered value\n suggestTypoCorrection = { enteredValue, possibleValues ->\n possibleValues.filter { it.startsWith(enteredValue) }\n }\n }\n }\n}\n
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.
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>) {\n val cli = Cli()\n try {\n cli.parse(args)\n } catch (e: CliktError) {\n cli.echoFormattedHelp(e)\n exitProcess(e.statusCode)\n }\n}\n
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 UsageErrors 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":"
See the [changelog] for a full list of changes in each release. This guide contains information on the most significant changes that may require updating to your code.
"},{"location":"migration/#upgrading-to-50","title":"Upgrading to 5.0","text":""},{"location":"migration/#some-methods-like-main-are-now-extensions","title":"some methods like main are now extensions","text":"
The CliktCommand.main and CliktCommand.parse methods are now extension functions, so you\u2019ll need to import them.
"},{"location":"migration/#cliktcommand-constructor-no-longer-takes-most-parameters","title":"CliktCommand constructor no longer takes most parameters","text":"
All parameters of the CliktCommand except for name have been moved to open properties.
In 5.0In 4.0
class MyCommand : CliktCommand(name=\"mycommand\") {\n override fun help(context: Context) = \"command help\"\n override fun helpEpilog(context: Context) = \"command epilog\"\n override val invokeWithoutSubcommand = true\n override val printHelpOnEmptyArgs = true\n override val helpTags = mapOf(\"tag\" to \"value\")\n override val autoCompleteEnvvar = \"MYCOMMAND_COMPLETE\"\n override val allowMultipleSubcommands = true\n override val treatUnknownOptionsAsArgs = true\n override val hiddenFromHelp = true\n}\n
class MyCommand : CliktCommand(\n name = \"mycommand\",\n help = \"command help\",\n helpEpilog = \"command epilog\",\n invokeWithoutSubcommand = true,\n printHelpOnEmptyArgs = true,\n helpTags = mapOf(\"tag\" to \"value\"),\n autoCompleteEnvvar = \"MYCOMMAND_COMPLETE\",\n allowMultipleSubcommands = true,\n treatUnknownOptionsAsArgs = true,\n hiddenFromHelp = true,\n) {\n}\n
The full list of moved parameters:
removed parameter new replacement property helpfun helpepilogfun helpEpiloginvokeWithoutSubcommandval invokeWithoutSubcommandprintHelpOnEmptyArgsval printHelpOnEmptyArgshelpTagsval helpTagsautoCompleteEnvvarval autoCompleteEnvvarallowMultipleSubcommandsval allowMultipleSubcommandstreatUnknownOptionsAsArgsval treatUnknownOptionsAsArgshiddenval hiddenFromHelp"},{"location":"migration/#markdown-moved-to-a-separate-module","title":"Markdown moved to a separate module","text":"
In order to reduce executable size, the Markdown rendering functionality has been moved to a separate module.
To use Markdown rendering first, add the :clitk-markdown dependency to your project:
Some of the properties on Context and its builder have been renamed to be more consistent:
old name new name Context.envvarReaderContext.readEnvvarContext.correctionSuggestorContext.suggestTypoCorrectionContext.argumentFileReaderContext.readArgumentFileContext.tokenTransformerContext.transformToken
The old names are still available as deprecated properties.
The remaining methods in TermUi have been removed. If you were using it, you can open an editor manually with ProcessBuilder or similar.
"},{"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\u2019s open methods are different. See the helpformat sample for an example of how to use the new formatter.
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\") {\n it.toIntOrNull()\n ?.let { ConversionResult.Valid(it) }\n ?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\n
val input = prompt(\"Enter a number\") {\n it.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.
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().
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.
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.
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() {\n val name by option(help=\"your name\")\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
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() {\n val name by option(\"-n\", \"--name\", help=\"your name\")\n override fun run() {\n echo(\"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:
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.
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() {\n val exp by option(\"-e\", \"--exp\").double().default(1.0)\n override fun run() {\n echo(\"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() {\n val square by option().int().pair()\n val cube by option().int().triple()\n val tesseract by option().int().transformValues(4) { Quad(it[0], it[1], it[2], it[3]) }\n override fun run() {\n echo(\"Square has dimensions ${square?.toList()?.joinToString(\"x\")}\")\n echo(\"Cube has dimensions ${cube?.toList()?.joinToString(\"x\")}\")\n echo(\"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() {\n val profiles by option(\"-P\", envvar=\"PROFILES\").split(\",\")\n override fun run() {\n for (profile in profiles) {\n echo(profile)\n }\n }\n}\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() {\n val log by option().optionalValue(\"debug\").default(\"none\")\n override fun run() {\n echo(\"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, you can use varargValues.
ExampleUsage
class Order : CliktCommand() {\n val sizes: List<String> by option().varargValues()\n override fun run() {\n echo(\"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.
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() {\n val message: List<String> by option(\"-m\").multiple()\n override fun run() {\n echo(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() {\n val platforms: Set<String> by option(\"-p\").multiple().unique()\n override fun run() {\n echo(\"Building for platforms: $platforms\")\n }\n}\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() {\n val systemProp: Map<String, String> by option(\"-D\", \"--system-prop\").associate()\n\n override fun run() {\n echo(systemProp)\n }\n}\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() {\n val flag by option(\"--on\", \"-o\").flag(\"--off\", \"-O\", default = false)\n override fun run() {\n echo(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() {\n val flagA by option(\"-a\").flag()\n val flagB by option(\"-b\").flag()\n val foo by option(\"-f\")\n override fun run() {\n echo(\"$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.
You can specify a limit for the number of times the counted option can be given, and either clamp the value or show an error if the limit is exceeded.
ExampleUsage
class Log : CliktCommand() {\n val verbosity by option(\"-v\").counted(limit=3, clamp=true)\n override fun run() {\n echo(\"Verbosity level: $verbosity\")\n }\n}\n
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() {\n val size by option().switch(\n \"--large\" to \"large\",\n \"--small\" to \"small\"\n ).default(\"unknown\")\n override fun run() {\n echo(\"You picked size $size\")\n }\n}\n
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 {\n data class Oranges(val size: String): Fruit()\n data class Apples(val count: Int): Fruit()\n}\nclass Order : CliktCommand() {\n val fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\n option(\"--oranges\").convert { Oranges(it) },\n option(\"--apples\").int().convert { Apples(it) }\n )\n\n override fun run() = echo(fruit)\n}\n
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() {\n val name by option().required()\n val age by option().int()\n}\nclass Tool : CliktCommand() {\n val userOptions by UserOptions().cooccurring()\n override fun run() {\n userOptions?.let {\n echo(it.name)\n echo(it.age)\n } ?: echo(\"No user options\")\n }\n}\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\") {\n val path by option().file().required()\n val followSymlinks by option().flag()\n}\n\nclass FromNetwork: LoadConfig(\"Options for loading from network\") {\n val url by option().required()\n val username by option().prompt()\n val password by option().prompt(hideInput = true)\n}\n\nclass Tool : CliktCommand() {\n val load by option().groupChoice(\n \"disk\" to FromDisk(),\n \"network\" to FromNetwork()\n )\n\n override fun run() {\n when(val it = load) {\n is FromDisk -> echo(\"Loading from disk: ${it.path}\")\n is FromNetwork -> echo(\"Loading from network: ${it.url}\")\n null -> 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=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() {\n val level by option(\"-l\", \"--level\", metavar = \"<number>\")\n .int(acceptsValueWithoutName = true)\n\n override fun run() {\n echo(\"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() {\n val name by option().prompt()\n override fun run() {\n echo(\"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.
Sometimes you want an option to halt execution immediately and print a message. For example, the built-in --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 these options don\u2019t have values, you can\u2019t define them using property delegates. Instead, call the function on a command directly, either in an init block, or on a command instance.
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() {\n init {\n eagerOption(\"--version\") {\n throw PrintMessage(\"$name 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 can\u2019t reference other options or arguments, since they\u2019re evaluated before parsing the rest of the command line. They can be declared in regular OptionGroups, but not in other types of groups like switch groups.
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() {\n val opt by option(help = \"option 1\").deprecated()\n val opt2 by option(help = \"option 2\").deprecated(\"WARNING: --opt2 is deprecated, use --new-opt instead\", tagName = null)\n val opt3 by option(help = \"option 3\").deprecated(tagName = \"pending deprecation\", tagValue = \"use --new-opt instead\")\n val opt4 by option(help = \"option 4\").deprecated(error = true)\n\n override 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
You may want to collect unknown options for manual processing. You can do this by overriding treatUnknownOptionsAsArgs = true in your command. 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() {\n init { context { allowInterspersedArgs = false }}\n override val treatUnknownOptionsAsArgs = true\n\n val command by option().required()\n val arguments by argument().multiple()\n\n override fun run() {\n val cmd = (listOf(command) + arguments).joinToString(\" \")\n val proc = Runtime.getRuntime().exec(cmd)\n echo(proc.inputStream.bufferedReader().readText())\n proc.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() {\n val name by option(envvar = \"MY_NAME\")\n override fun run() {\n echo(\"Hello $name\")\n }\n}\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() {\n init {\n context { autoEnvvarPrefix = \"HELLO\" }\n }\n val name by option()\n override fun run() {\n echo(\"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.readEnvvar.
@Test\nfun `test envvar`() {\n val envvars = mapOf(\"MY_TOOL_OPTION\" to \"value\")\n val tool = MyTool().context {\n readEnvvar = { envvars[it] }\n }\n tool.parse(emptyList())\n assertEquals(\"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() {\n init {\n context {\n valueSource = PropertiesValueSource.from(\"myconfig.properties\")\n }\n }\n val name by option()\n override fun run() {\n echo(\"Hello $name\")\n }\n}\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() {\n val name by option(\"/name\", help=\"your name\")\n override fun run() {\n echo(\"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() {\n val name by option(\"-name\", help=\"your name\")\n override fun run() {\n echo(\"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.
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
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.
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.
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() {\n val inferredOpt by option()\n val inferred by argument()\n val explicitOpt by option(\"-e\", \"--explicit\")\n val explicitArg by argument(\"<explicit>\")\n override 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
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().
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.
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.
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 OutputStreams","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.
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() {\n val opt by option().convert { it.toBigDecimal() }\n override 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
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() {\n val opt by option(help=\"a real number\").convert(\"float\") {\n it.toBigDecimalOrNull() ?: fail(\"A real number is required\")\n }\n override 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
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() {\n val file: String by argument()\n .file(mustExist=true, canBeDir=false)\n .convert { it.readText() }\n override fun run() {\n echo(\"Your file contents: $file\")\n }\n}\n
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).
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() {\n val number by option(help = \"An even number\").int()\n .check(\"value must be even\") { it % 2 == 0 }\n\n override fun run() {\n echo(\"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
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() {\n val number by option().int().default(0)\n val biggerNumber by option().int().validate {\n require(it > number) {\n \"--bigger-number must be bigger than --number\"\n }\n }\n\n override fun run() {\n echo(\"number=$number, biggerNumber=$biggerNumber\")\n }\n}\n
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:
$ ./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/#coroutine-commands-with-suspend-functions","title":"Coroutine commands with suspend functions","text":"
If you want to use coroutines in your command, you can use a SuspendingCliktCommand:
class Hello : SuspendingCliktCommand() {\n val count by option(help=\"Number of greetings\").int().default(1)\n val name by argument()\n\n override suspend fun run() {\n for (i in 1..count) {\n echo(\"Hello $name!\")\n delay(1000)\n }\n }\n}\n\nsuspend fun main(args: Array<String>) = Hello().main(args)\n
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\") {\n override fun run() = Unit\n}\n\nclass Init : CliktCommand() {\n override fun help(context: Context) = \"Initialize the database\"\n override fun run() {\n echo(\"Initialized the database.\")\n }\n}\n\nclass Drop : CliktCommand() {\n override fun help(context: Context) = \"Drop the database\"\n override fun run() {\n echo(\"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
To add parameters, use the option and argument property delegates:
ExampleHelp Output
class Hello : CliktCommand() {\n val count by option(help=\"Number of greetings\").int().default(1)\n val name by argument()\n\n override fun run() {\n for (i in 1..count) {\n echo(\"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.
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.
Clikt includes the test extension to help testing commands and their output.
TestCommand
@Test\nfun testHello() {\n val command = Hello()\n val result = command.test(\"--name Foo\")\n assertEqual(result.stdout, \"Hello, Foo!\")\n assertEqual(result.exitCode, 0)\n assertEqual(command.name, \"Foo\")\n}\n
class Hello: CliktCommand() {\n val name by option()\n override fun run() {\n echo(\"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.
You can set environment variables for your command by passing in a map of envvars.
TestCommand
@Test\nfun testHello() {\n val command = Hello()\n val result = command.test(\"\", envvars=mapOf(\"HELLO_NAME\" to \"Foo\"))\n assertEqual(result.stdout, \"Hello, Foo!\")\n}\n
class Hello: CliktCommand() {\n val name by option(envvar=\"HELLO_NAME\")\n override fun run() {\n echo(\"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.
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() {\n val command = Adder()\n val result = command.test(\"\", stdin = \"2\\n3\")\n assertEqual(result.stdout, \"first: second: result: 2 + 3 = 5\")\n}\n
class Adder : TestCommand() {\n val first by option().prompt()\n val second by option().prompt()\n\n override fun run_() {\n echo(\"result: $first + $second = ${first + second}\")\n }\n}\n
CliktCommand.main calls Context.exitProcess to exit the process. You can set that to an empty lambda to skip it, or one that captures the status value if you want to check it in you tests.
"},{"location":"testing/#using-parse-instead-of-main","title":"Using parse instead of main","text":"
Instead of calling main, you can 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.
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.
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? {\n val message = \"\"\"\n # Enter your message.\n # Lines starting with # are ignored\n \"\"\".trimIndent()\n return editText(message, requireSave = true)\n ?.replace(Regex(\"#[^\\n]*\\n\"), \"\")\n}\n
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\") {\n it.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
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\u2019s 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) {\n val v: Boolean by parser.flagging(help=\"enable verbose mode\")\n val username: String? by parser.storing(help=\"name of the user\")\n val count: Int? by parser.storing(help=\"number of the widgets\") { toInt() }\n val source: List<String> by parser.positionalList(help=\"source filenames\")\n val destination: String by parser.positional(help=\"destination\")\n}\n\nfun main(args: Array<String>) = mainBody {\n ArgParser(args).parseInto(::MyArgs).run {\n println(\"Hello, $username!\")\n println(\"Moving $count widgets from $source to $destination.\")\n }\n}\n
Here\u2019s the same thing with Clikt:
class Cli : CliktCommand() {\n val v: Boolean by option(help = \"enable verbose mode\").flag()\n val username: String? by option(help = \"name of the user\")\n val count: Int? by option(help = \"number of the widgets\").int()\n val source: List<String> by argument(help = \"source filenames\").multiple()\n val destination: String by argument(help = \"destination\")\n override fun run() {\n println(\"Hello, $name!\")\n println(\"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) =\n option<MutableMap<String, String>>(*names,\n argNames = listOf(\"KEY\", \"VALUE\"),\n help = help) {\n value.orElse { mutableMapOf<String, String>() }.apply {\n put(arguments.first(), arguments.last()) }\n }\n\n fun ArgParser.putting(help: String) =\n ArgParser.DelegateProvider { identifier ->\n putting(identifierToOptionName(identifier), help = help) }\n\nclass MyArgs(parser: ArgParser) {\n val 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() {\n val 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() {\n val count: Int by option().int().default(1).help(\"Number of greetings\")\n val name: String by option().prompt(\"Your name\").help(\"The person to greet\")\n\n override fun run() {\n repeat(count) {\n echo(\"Hello $name!\")\n }\n }\n}\n\nfun main(args: Array<String>) = Hello().main(args)\n
dependencies {\n implementation(\"com.github.ajalt.clikt:clikt:5.0.0\")\n\n // optional support for rendering markdown in help messages\n implementation(\"com.github.ajalt.clikt:clikt-markdown:5.0.0\")\n}\n
There is also a smaller core module available. See the docs for details.
"},{"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 most multiplatform targets. 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:
"},{"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() {\n private val token by option(help=\"api token to use for requests\").default(\"...\")\n private val hostname by option(help=\"base url for requests\").default(\"example.com\")\n\n override fun run() {\n currentContext.obj = Config(token, hostname)\n }\n}\n\nclass Store : CliktCommand() {\n private val file by option(help=\"file to store\").file(canBeDir = false)\n private val config by requireObject<Config>()\n override fun run() {\n myApiStoreFile(config.token, config.hostname, file)\n }\n}\n\nclass Fetch : CliktCommand() {\n private val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\n private val config by requireObject<Config>()\n override fun run() {\n myApiFetchFile(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
"},{"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:\") {\n val token by option(help=\"api token to use for requests\").default(\"...\")\n val hostname by option(help=\"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : CliktCommand() {\n private val commonOptions by CommonOptions()\n private val file by option(help=\"file to store\").file(canBeDir = false)\n override fun run() {\n myApiStoreFile(commonOptions.token, commonOptions.hostname, file)\n }\n}\n\nclass Fetch : CliktCommand() {\n private val commonOptions by CommonOptions()\n private val outdir by option(help=\"directory to store file in\").file(canBeFile = false)\n override fun run() {\n myApiFetchFile(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
"},{"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() {\n val token by option(help = \"api token to use for requests\").default(\"...\")\n val hostname by option(help = \"base url for requests\").default(\"example.com\")\n}\n\nclass MyApi : NoOpCliktCommand()\n\nclass Store : MyApiSubcommand() {\n private val file by option(help = \"file to store\").file(canBeDir = false)\n override fun run() {\n myApiStoreFile(token, hostname, file)\n }\n}\n\nclass Fetch : MyApiSubcommand() {\n private val outdir by option(help = \"directory to store file in\").file(canBeFile = false)\n override fun run() {\n myApiFetchFile(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
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.\n override fun aliases(): Map<String, List<String>> = mapOf(\n \"ci\" to listOf(\"commit\"),\n \"cm\" to listOf(\"commit\", \"-m\")\n )\n}\n\nclass Commit: CliktCommand() {\n val message by option(\"-m\").default(\"\")\n override fun run() {\n echo(\"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() {\n override fun aliases(): Map<String, List<String>> {\n val prefixCounts = mutableMapOf<String, Int>().withDefault { 0 }\n val prefixes = mutableMapOf<String, List<String>>()\n for (name in registeredSubcommandNames()) {\n if (name.length < 3) continue\n for (i in 1..name.lastIndex) {\n val prefix = name.substring(0..i)\n prefixCounts[prefix] = prefixCounts.getValue(prefix) + 1\n prefixes[prefix] = listOf(name)\n }\n }\n return prefixes.filterKeys { prefixCounts.getValue(it) == 1 }\n }\n}\n\nclass Foo: CliktCommand() {\n override fun run() {\n echo(\"Running Foo\")\n }\n}\n\nclass Bar: CliktCommand() {\n override fun run() {\n echo(\"Running Bar\")\n }\n}\n\nfun main(args: Array<String>) = Tool().subcommands(Foo(), Bar()).main(args)\n
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 transformToken on the command\u2019s context, which will be called for each option and command name that\u2019s input. This can be used to implement case-insensitive parsing, for example:
ExampleUsage
class Hello : CliktCommand() {\n init {\n context { transformToken = { it.lowercase() } }\n }\n\n val name by option()\n override 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.
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:
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.
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.
You might need to open a resource like a file or a network connection in one command, and use it in its subcommands.
The typical way to manage a resource is with the use function:
class MyCommand : CliktCommand() {\n private val file by option().file().required()\n\n override fun run() {\n file.bufferedReader().use { reader ->\n // use the reader\n }\n }\n}\n
But if you need to share the resource with subcommands, the use function will exit and close the resource before the subcommand is called. Instead, use the context\u2019s registerCloseable function (for kotlin.AutoCloseable) or registerJvmCloseable function (for java.lang.AutoCloseable) to:
class MyCommand : CliktCommand() {\n private val file by option().file().required()\n\n override fun run() {\n currentContext.obj = currentContext.registerJvmCloseable(file.bufferedReader())\n }\n}\n
You can register as many closeables as you need, and they will all be closed when the command and its subcommands have finished running. If you need to manage a resource that isn\u2019t AutoClosable, you can use callOnClose.
"},{"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/#custom-run-function-signature","title":"Custom run function signature","text":"
Clikt provides a few command base classes that have different run function signatures. CliktCommand has fun run(), while SuspendingCliktCommand has suspend fun run(). If you want a run function with a different signature, you can define your own base class the inherits from BaseCliktCommand and use the CommandLineParser methods to parse and run the command.
For example, if you want a command that uses a Flow to emit multiple value for each run, you could implement it like this:
ExampleUsage
abstract class FlowCliktCommand : BaseCliktCommand<FlowCliktCommand>() {\n abstract fun run(): Flow<String>\n}\n\nclass MyFlowCommand : FlowCliktCommand() {\n val opt by option().required()\n val arg by argument().int()\n\n override fun run(): Flow<String> = flow {\n emit(opt)\n emit(arg.toString())\n }\n}\n\nclass MyFlowSubcommand : FlowCliktCommand() {\n val arg by argument().multiple()\n\n override fun run(): Flow<String> = flow {\n arg.forEach { emit(it) }\n }\n}\n\nfun FlowCliktCommand.parse(argv: Array<String>): Flow<String> {\n val flows = mutableListOf<Flow<String>>()\n CommandLineParser.parseAndRun(this, argv.asList()) { flows += it.run() }\n return flow { flows.forEach { emitAll(it) } }\n}\n\nfun FlowCliktCommand.main(argv: Array<String>): Flow<String> {\n return CommandLineParser.mainReturningValue(this) { parse(argv) }\n}\n\nsuspend fun main(args: Array<String>) {\n val command = MyFlowCommand().subcommands(MyFlowSubcommand())\n val resultFlow: Flow<String> = command.main(args)\n resultFlow.collect {\n command.echo(it)\n }\n}\n
$ ./command --opt=foo 11 my-flow-subcommand bar baz\nfoo\n11\nbar\nbaz\n
There are a number of steps here, so let\u2019s break it down:
Define a base class FlowCliktCommand that inherits from BaseCliktCommand and has an abstract run function that returns a Flow.
Define your commands that inherit from FlowCliktCommand and implement your run function.
Define an extension function parse that uses CommandLineParser.parseAndRun to parse the command and run the run function.
Define an extension function main that uses CommandLineParser.main to run the parse function and handle any exceptions it might throw.
In your main function, call main on your command, and collect the results of the Flow.
If you want to customize the behavior even further, see the next section.
"},{"location":"advanced/#custom-run-behavior","title":"Custom run behavior","text":"
If you want to customize how or when subcommands are run, you can do so by defining a custom base class like in the previous section, but instead of using CommandLineParser.parseAndRun, you can call your command\u2019s run functions manually.
For example, if you want commands to return status codes, and you want to stop running commands as soon as one of them returns a non-zero status code, you could implement it like this:
ExampleUsage
abstract class StatusCliktCommand : BaseCliktCommand<StatusCliktCommand>() {\n abstract fun run(): Int\n}\n\nclass ParentCommand : StatusCliktCommand() {\n override val allowMultipleSubcommands: Boolean = true\n override fun run(): Int {\n echo(\"Parent\")\n return 0\n }\n}\n\nclass SuccessCommand : StatusCliktCommand() {\n override fun run(): Int {\n echo(\"Success\")\n return 0\n }\n}\n\nclass FailureCommand : StatusCliktCommand() {\n override fun run(): Int {\n echo(\"Failure\")\n return 1001\n }\n}\n\nfun StatusCliktCommand.parse(argv: Array<String>): Int {\n val parseResult = CommandLineParser.parse(this, argv.asList())\n parseResult.invocation.flatten().use { invocations ->\n for (invocation in invocations) {\n val status = invocation.command.run()\n if (status != 0) {\n return status\n }\n }\n }\n return 0\n}\n\nfun StatusCliktCommand.main(argv: Array<String>) {\n val status = CommandLineParser.mainReturningValue(this) { parse(argv) }\n exitProcess(status)\n}\n\nfun main(argv: Array<String>) {\n ParentCommand().subcommands(SuccessCommand(), FailureCommand()).main(argv)\n}\n
The steps here are similar to the previous section, but instead of using CommandLineParser.parseAndRun, we use CommandLineParser.parse, then call run on each command invocation manually, and stop when one of them returns a non-zero status code.
Clikt normally uses Mordant for rendering output and interacting with the system, but there are some cases where you might want to use Clikt without Mordant. For these cases, Clikt has a core module that doesn\u2019t have any dependencies.
Replace your Clikt dependency with the core module:
The CliktCommand class is only available in the full module, so you\u2019ll need to use CoreCliktCommand (or CoreNoOpCliktCommand) instead. The CoreCliktCommand has the same API as CliktCommand, but it doesn\u2019t have any of these features built in:
Text wrapping, formatting, markdown, or color support
argument files
environment variables
main exiting the process with a status code
printing to stderr
prompt options
The test function
Most of those features can be added by setting the appropriate properties on the command\u2019s context. Here\u2019s an example of setting all of them using Java APIs, but you only need to set the ones you\u2019ll use:
There are some JVM-only extensions available such as file and path parameter types.
"},{"location":"advanced/#desktop-native-linux-windows-and-macos","title":"Desktop native (Linux, Windows, and macOS)","text":""},{"location":"advanced/#javascript-and-wasmjs","title":"JavaScript and WasmJS","text":"
All functionality is supported on Node.js.
In the browser, 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.
"},{"location":"advanced/#ios","title":"iOS","text":""},{"location":"advanced/#watchos-tvos-and-wasmwasi","title":"watchOS, tvOS and WasmWasi","text":"
These platforms are not supported by the markdown module, but all other functionality is available.
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.
By default, argument takes a single String value which is required to be provided on the command line.
ExampleUsage
class Hello : CliktCommand() {\n val name by argument()\n override fun run() {\n echo(\"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(\n help = \"Copy <source> to <dest>, or multiple <source>(s) to directory <dest>.\"\n) {\n private val source by argument().file(mustExist = true).multiple()\n private val dest by argument().file()\n override 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
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() {\n val source: List<Path> by argument().path(mustExist = true).multiple()\n val dest: Path by argument().path(canBeFile = false)\n override fun run() {\n echo(\"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
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() {\n val verbose by option().flag()\n val files by argument().multiple()\n override fun run() {\n if (verbose) echo(files.joinToString(\"\\n\"))\n }\n}\n
$ ./touch --foo.txt\nUsage: touch [<options>] [<files>]...\n\nError: no such option: \"--foo.txt\".\n
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 by overriding the autoCompleteEnvvar property in your command. 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:
"},{"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() {\n init {\n completionOption()\n }\n // ...\n}\n
class MyCommand: CliktCommand() {\n //..\n}\n\nfun main(args: Array<String>) = MyCommand().completionOption().main(args)\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.
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.
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.\n val name by option(completionCandidates =\n CompletionCandidates.Custom.fromStdout(\"echo completion1 completion2\")\n )\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
class Hello: CliktCommand() {\n // This is identical to the previous example\n val name by option(completionCandidates = CompletionCandidates.Custom {\n \"\"\"\n WORDS=${'$'}(echo completion1 completion2)\n COMPREPLY=(${'$'}(compgen -W \"${'$'}WORDS\" -- \"${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}\"))\n \"\"\".trimIndent()\n })\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
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.
Added NoSuchArgument exception that is thrown when too many arguments were given on the command line. Previously, a less specific UsageError was thrown instead.
Added CommandLineParser.tokenize that splits a string into argv tokens.
Added CommandLineParser that provides functions for parsing and finalizing commands manually for more control.
Added Context.invokedSubcommands that contains all subcommands of the current command that are going to be invoked when allowMultipleSubcommands is true.
Added SuspendingCliktCommand that has a suspend fun run method, allowing you to use coroutines in your commands.
Added ChainedCliktCommand that allows you to return a value from your run method and pass it to the next command in the chain.
Added Context.data as an alternative to obj that allows you to store more than one object in the context.
Added Context.echoer to customize how echo messages are printed.
Added CompletionGenerator to manually generate completions for a command.
Added Context.exitProcess which you can use to prevent the process from exiting during tests.
Added core module that supports watchOS, tvOS, and wasmWasi targets and has no dependencies.
Added more options to CliktCommand.test to control the terminal interactivity. (#517)
Added associate{}, associateBy{}, and associateWith{} transforms for options that allow you to convert the keys and values of the map. (#529)
Added support for aliasing options to other options. (#535)
Added limit and ignoreCase parameters to option().split(). (#541)
Support calling --help on subcommands when parents have required parameters.
In a subcommand with and an argument() with multiple() or optional(), the behavior is now the same regardless of the value of allowMultipleSubcommands: if a token matches a subcommand name, it\u2019s now treated as a subcommand rather than a positional argument.
Due to changes to the internal parsing algorithm, the exact details of error messages when multiple usage errors occur have changed in some cases.
Breaking Change: Moved the following parameters from CliktCommand\u2019s constructor; override the corresponding properties instead:
removed parameter replacement property helpfun helpepilogfun helpEpiloginvokeWithoutSubcommandval invokeWithoutSubcommandprintHelpOnEmptyArgsval printHelpOnEmptyArgshelpTagsval helpTagsautoCompleteEnvvarval autoCompleteEnvvarallowMultipleSubcommandsval allowMultipleSubcommandstreatUnknownOptionsAsArgsval treatUnknownOptionsAsArgshiddenval hiddenFromHelp - The following methods on CliktCommand have been renamed: commandHelp -> help, commandHelpEpilog -> epilog. The old names are deprecated. - Breaking Change: CliktCommand.main and CliktCommand.parse are now extension functions rather than methods. - Breaking Change: Context.obj and Context.terminal, and OptionTransformContext.terminal are now extension functions rather than properties. - Breaking Change: The RenderedSection and DefinitionRow classes have moved to AbstractHelpFormatter. - Markdown support in the help formatter is no longer included by default. To enable it, include the :clikt-markdown dependency and call yourCommand.installMordantMarkdown() before parsing. - Updated Kotlin to 2.0.0"},{"location":"changelog/#fixed","title":"Fixed","text":"
Fixed excess arguments not being reported when allowMultipleSubcommands=true and a subcommand has excess arguments followed by another subcommand.
Commands with printHelpOnEmptyArgs=true will no longer print help if an option has a value from an environment variable or value source. (#382)
Deprecated Context.originalArgv. It will now always return an empty list. If your commands need an argv, you can pass it to them before you run them, or set in on the new Context.data map.
Deprecated Context.expandArgumentFiles. Use Context.argumentFileReader instead.
Renamed the following Context fields to be more consistent. The old names are deprecated.
old name new name Context.envvarReaderContext.readEnvvarContext.correctionSuggestorContext.suggestTypoCorrectionContext.argumentFileReaderContext.readArgumentFileContext.tokenTransformerContext.transformToken"},{"location":"changelog/#removed","title":"Removed","text":"
Added limit parameter to option().counted() to limit the number of times the option can be used. You can either clamp the value to the limit, or throw an error if the limit is exceeded. (#483)
Added Context.registerClosable and Context.callOnClose to allow you to register cleanup actions that will be called when the command exits. (#395)
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)
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)
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.
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.
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>.
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
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.
InputStream.isCliktParameterDefaultStdin and OutputStream.isCliktParameterDefaultStdout to check if the streams returned from inputStream/outputStream options are proxying stdin/stdout (#272)
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.
@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)
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)
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.
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.
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)
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)
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
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()
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.
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)
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)
CompletionCandidates.Fixed now has a secondary convenience constructor that take a vararg of Strings
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.
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)
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.
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
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.
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.
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() {\n val verbose by option().flag(\"--no-verbose\")\n override fun run() {\n echo(\"Verbose mode is ${if (verbose) \"on\" else \"off\"}\")\n }\n}\n\nclass Execute : CliktCommand() {\n override fun run() {\n echo(\"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
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() {\n override fun run()= Unit\n}\n\nclass Execute : CliktCommand(name = \"RUN-ME\") {\n override fun run() {\n echo(\"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
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() {\n override fun help(context: Context) = \"A tool that runs\"\n val verbose by option().flag(\"--no-verbose\")\n override fun run() = Unit\n}\n\nclass Execute : CliktCommand() {\n override fun help(context: Context) = \"Execute the command\"\n val name by option()\n override 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\u2019s 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 a [data][Context.data] map and obj property that hold objects that can be accessed from child commands.
ExampleUsage
data class MyConfig(var verbose: Boolean = false)\n\nclass Tool : CliktCommand() {\n val verbose by option().flag(\"--no-verbose\")\n val config by findOrSetObject { MyConfig() }\n override fun run() {\n config.verbose = if (verbose) \"on\" else \"off\"\n }\n}\n\nclass Execute : CliktCommand() {\n val config by requireObject<MyConfig>()\n override fun run() {\n echo(\"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. If you need more than one object, you can pass a key to these functions, and they\u2019ll look for an object with that key and type in the context\u2019s data map.
Keep in mind 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() {\n override fun run() {\n // runs eagerly\n currentContext.findOrSetObject { MyConfig() }\n }\n}\n
class Tool : CliktCommand() {\n override fun run() {\n // runs eagerly, won't look for parent contexts\n currentContext.obj = MyConfig()\n }\n}\n
If you need to share resources that need to be cleaned up, you can use currentContext.registerCloseable
"},{"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() {\n override val invokeWithoutSubcommand = true\n override fun run() {\n val subcommand = currentContext.invokedSubcommand\n if (subcommand == null) {\n echo(\"invoked without a subcommand\")\n } else {\n echo(\"about to run ${subcommand.name}\")\n }\n }\n}\n\nclass Execute : CliktCommand() {\n override fun run() {\n echo(\"running subcommand\")\n }\n}\n
$ ./tool\ninvoked without a subcommand\n
$./tool execute\nabout to run execute\nrunning subcommand\n
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:
$ ./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 overriding printHelpOnEmptyArgs = true in your command. 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() {\n override val printHelpOnEmptyArgs = true\n val arg by argument()\n override 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\n val opt by option().validate {\n if (it.isEmpty()) message(\"Empty strings are not recommended\")\n }\n override fun run() {\n echo(\"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() {\n init { context { printExtraMessages = false } }\n val opt by option().validate {\n if (it.isEmpty()) message(\"Empty strings are not recommended\")\n }\n override fun run() {\n echo(\"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, override allowMultipleSubcommands = true in your command.
ExampleUsage
class Compiler: CliktCommand() {\n override val allowMultipleSubcommands = true\n override fun run() {\n echo(\"Running compiler\")\n }\n}\n\nclass Clean: CliktCommand() {\n val force by option().flag()\n override fun run() {\n echo(\"Cleaning (force=$force)\")\n }\n}\n\nclass Build: CliktCommand() {\n val file by argument().file()\n override fun run() {\n echo(\"Building $file\")\n }\n}\n\nfun main(args: Array<String>) = Compiler().subcommands(Clean(), Build()).main(args)\n
The parent command will run once, and each subcommand will run once each time they\u2019re called.
Warning
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.
Subcommands of a command with allowMultipleSubcommands=true can themselves have subcommands, but cannot have allowMultipleSubcommands=true.
If you have multiple subcommands you might want to pass the output of one subcommand to the next. For example, the ImageMagick tool lets you apply a series of transformations to an image by invoking multiple subcommands.
To do this with Clikt, you could pass your output through the Context.obj, but another option is to use a ChainedCliktCommand, which allows you to return values from your run function that will be passed to the next subcommand.
In this example, we\u2019ll write simple text editing pipeline that takes an initial string, and then applies a series of transformations to it, printing the final result:
ExampleUsage
class EditText : ChainedCliktCommand<String>() {\n override val allowMultipleSubcommands: Boolean = true\n val text by argument()\n override fun run(value: String): String = text\n}\n\nclass RepeatText : ChainedCliktCommand<String>(\"repeat\") {\n val count by option().int().default(1)\n override fun run(value: String): String {\n return value.repeat(count)\n }\n}\n\nclass UppercaseText : ChainedCliktCommand<String>(\"uppercase\") {\n override fun run(value: String): String {\n return value.uppercase()\n }\n}\n\nclass ReplaceText : ChainedCliktCommand<String>(\"replace\") {\n val oldValue by argument()\n val newValue by argument()\n override fun run(value: String): String {\n return value.replace(oldValue, newValue)\n }\n}\n\nfun main(args: Array<String>) {\n val command = EditText()\n .subcommands(RepeatText(), UppercaseText(), ReplaceText())\n val result = command.main(args, \"\")\n command.echo(result)\n}\n
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.
You can add help text to commands and parameters. For parameters, you can pass a help string or use the help() extension. For commands, you can override the help and helpEpilog methods.
ExampleAlternate styleHelp output
class Hello : CliktCommand() {\n override fun help(context: Context) = \"\"\"\n This script prints <name> <count> times.\n\n <count> must be a positive number, and defaults to 1.\n \"\"\".trimIndent()\n val count by option(\"-c\", \"--count\", metavar=\"count\", help=\"number of greetings\")\n .int().default(1)\n val name by argument(help=\"The name to greet\")\n override fun run() = repeat(count) { echo(\"Hello $commandName!\") }\n}\n
class Hello : CliktCommand() {\n override fun help(context: Context): String {\n val style = context.theme.info\n return \"\"\"\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\n val count by option(\"-c\", \"--count\", metavar=\"count\").int().default(1)\n .help { theme.success(\"number of greetings\") }\n val name by argument()\n .help(\"The name to greet\")\n override 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":"
You can configure Clikt to use Mordant to render Markdown in help texts. You can use all the normal markdown features, such as lists, tables, and even hyperlinks if your terminal supports them.
First, add the :clitk-markdown dependency to your project:
And install the markdown help formatter on your command:
val command = MyCommand().installMordantMarkdown()\n
Then you can use markdown in your help strings:
ExampleHelp output
class Tool : NoOpCliktCommand() {\n init {\n installMordantMarkdown()\n }\n val 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() {\n val 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() {\n override fun help(context: Context) = \"\"\"\n Execute the command.\n\n The command will be executed.\n \"\"\".trimIndent()\n}\n\nclass Abort : NoOpCliktCommand() {\n override fun help(context: Context) = \"Kill any running commands.\"\n}\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
Clikt adds a help option to every command automatically. It uses the names -h and --help and prints the command\u2019s help message when invoked.
"},{"location":"documenting/#changing-the-help-option-names","title":"Changing the help option names","text":"
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 {\n override fun helpOptionMessage(): String = \"show the help\"\n}\n\nclass Tool : NoOpCliktCommand() {\n init {\n context {\n helpOptionNames = setOf(\"/help\")\n localization = 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/#changing-the-help-option-behavior","title":"Changing the help option behavior","text":"
If you want to run some code when the help option is invoked, or change its behavior, you can define the option yourself. The default help option is an eager option that throws a PrintHelpMessage, so if you wanted to log some information when the help option is invoked, you could do something like this:
ExampleExample 2Usage
class CustomHelpCommand : TestCommand() {\n init {\n eagerOption(\"-h\", \"--help\", help=\"Show this message and exit\") {\n echo(\"about to print help\")\n throw PrintHelpMessage(context)\n }\n }\n}\n
// If you want to use the help message from the localization, you can register an option\n// with eager=true and use the lazy `help` method.\nclass CustomHelpCommand : TestCommand() {\n init {\n registerOption(\n option(\"-h\", \"--help\", eager=true).flag()\n .help { context.localization.helpOptionMessage() }\n .validate {\n if(it) {\n echo(\"about to print help\")\n throw PrintHelpMessage(context)\n }\n }\n )\n }\n}\n
$ ./tool --help\nabout to print help\nUsage: custom-help [<options>]\n\nOptions:\n -h, --help Show this message and exit\n
Warning
Eager options can\u2019t reference other options or arguments, since they\u2019re evaluated before parsing the rest of the command line.
"},{"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() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, showDefaultValues = true) }\n }\n }\n\n val a by option(help = \"this is optional\").default(\"value\")\n val 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.
You can pass a character to the requiredOptionMarker argument of the MordantHelpFormatter.
ExampleUsage
class Tool : NoOpCliktCommand() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, requiredOptionMarker = \"*\") }\n }\n }\n\n val option by option(help = \"this is optional\")\n val 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
You can also show a tag for required options by passing showRequiredTag = true to the MordantHelpFormatter.
ExampleUsage
class Tool : CliktCommand() {\n init {\n context {\n helpFormatter = { MordantHelpFormatter(it, showRequiredTag = true) }\n }\n }\n\n val option by option(help = \"this is optional\")\n val 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(\n name = \"User Options\",\n help = \"Options controlling the user\"\n) {\n val name by option(help = \"user name\")\n val age by option(help = \"user age\").int()\n}\n\nclass Tool : NoOpCliktCommand() {\n val 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 suggestTypoCorrection on your command\u2019s context.
class Cli : NoOpCliktCommand() {\n init {\n context {\n // Only suggest corrections that start with the entered value\n suggestTypoCorrection = { enteredValue, possibleValues ->\n possibleValues.filter { it.startsWith(enteredValue) }\n }\n }\n }\n}\n
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.
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>) {\n val cli = Cli()\n try {\n cli.parse(args)\n } catch (e: CliktError) {\n cli.echoFormattedHelp(e)\n exitProcess(e.statusCode)\n }\n}\n
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 UsageErrors 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":"
See the [changelog] for a full list of changes in each release. This guide contains information on the most significant changes that may require updating to your code.
"},{"location":"migration/#upgrading-to-50","title":"Upgrading to 5.0","text":""},{"location":"migration/#some-methods-like-main-are-now-extensions","title":"some methods like main are now extensions","text":"
The CliktCommand.main and CliktCommand.parse methods are now extension functions, so you\u2019ll need to import them.
"},{"location":"migration/#cliktcommand-constructor-no-longer-takes-most-parameters","title":"CliktCommand constructor no longer takes most parameters","text":"
All parameters of the CliktCommand except for name have been moved to open properties.
In 5.0In 4.0
class MyCommand : CliktCommand(name=\"mycommand\") {\n override fun help(context: Context) = \"command help\"\n override fun helpEpilog(context: Context) = \"command epilog\"\n override val invokeWithoutSubcommand = true\n override val printHelpOnEmptyArgs = true\n override val helpTags = mapOf(\"tag\" to \"value\")\n override val autoCompleteEnvvar = \"MYCOMMAND_COMPLETE\"\n override val allowMultipleSubcommands = true\n override val treatUnknownOptionsAsArgs = true\n override val hiddenFromHelp = true\n}\n
class MyCommand : CliktCommand(\n name = \"mycommand\",\n help = \"command help\",\n helpEpilog = \"command epilog\",\n invokeWithoutSubcommand = true,\n printHelpOnEmptyArgs = true,\n helpTags = mapOf(\"tag\" to \"value\"),\n autoCompleteEnvvar = \"MYCOMMAND_COMPLETE\",\n allowMultipleSubcommands = true,\n treatUnknownOptionsAsArgs = true,\n hiddenFromHelp = true,\n) {\n}\n
The full list of moved parameters:
removed parameter new replacement property helpfun helpepilogfun helpEpiloginvokeWithoutSubcommandval invokeWithoutSubcommandprintHelpOnEmptyArgsval printHelpOnEmptyArgshelpTagsval helpTagsautoCompleteEnvvarval autoCompleteEnvvarallowMultipleSubcommandsval allowMultipleSubcommandstreatUnknownOptionsAsArgsval treatUnknownOptionsAsArgshiddenval hiddenFromHelp"},{"location":"migration/#markdown-moved-to-a-separate-module","title":"Markdown moved to a separate module","text":"
In order to reduce executable size, the Markdown rendering functionality has been moved to a separate module.
To use Markdown rendering first, add the :clitk-markdown dependency to your project:
Some of the properties on Context and its builder have been renamed to be more consistent:
old name new name Context.envvarReaderContext.readEnvvarContext.correctionSuggestorContext.suggestTypoCorrectionContext.argumentFileReaderContext.readArgumentFileContext.tokenTransformerContext.transformToken
The old names are still available as deprecated properties.
The remaining methods in TermUi have been removed. If you were using it, you can open an editor manually with ProcessBuilder or similar.
"},{"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\u2019s open methods are different. See the helpformat sample for an example of how to use the new formatter.
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\") {\n it.toIntOrNull()\n ?.let { ConversionResult.Valid(it) }\n ?: ConversionResult.Invalid(\"$it is not a valid integer\")\n}\n
val input = prompt(\"Enter a number\") {\n it.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.
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().
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.
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.
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() {\n val name by option(help=\"your name\")\n override fun run() {\n echo(\"Hello, $name!\")\n }\n}\n
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() {\n val name by option(\"-n\", \"--name\", help=\"your name\")\n override fun run() {\n echo(\"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:
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.
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() {\n val exp by option(\"-e\", \"--exp\").double().default(1.0)\n override fun run() {\n echo(\"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() {\n val square by option().int().pair()\n val cube by option().int().triple()\n val tesseract by option().int().transformValues(4) { Quad(it[0], it[1], it[2], it[3]) }\n override fun run() {\n echo(\"Square has dimensions ${square?.toList()?.joinToString(\"x\")}\")\n echo(\"Cube has dimensions ${cube?.toList()?.joinToString(\"x\")}\")\n echo(\"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() {\n val profiles by option(\"-P\", envvar=\"PROFILES\").split(\",\")\n override fun run() {\n for (profile in profiles) {\n echo(profile)\n }\n }\n}\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() {\n val log by option().optionalValue(\"debug\").default(\"none\")\n override fun run() {\n echo(\"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, you can use varargValues.
ExampleUsage
class Order : CliktCommand() {\n val sizes: List<String> by option().varargValues()\n override fun run() {\n echo(\"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.
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() {\n val message: List<String> by option(\"-m\").multiple()\n override fun run() {\n echo(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() {\n val platforms: Set<String> by option(\"-p\").multiple().unique()\n override fun run() {\n echo(\"Building for platforms: $platforms\")\n }\n}\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() {\n val systemProp: Map<String, String> by option(\"-D\", \"--system-prop\").associate()\n\n override fun run() {\n echo(systemProp)\n }\n}\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() {\n val flag by option(\"--on\", \"-o\").flag(\"--off\", \"-O\", default = false)\n override fun run() {\n echo(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() {\n val flagA by option(\"-a\").flag()\n val flagB by option(\"-b\").flag()\n val foo by option(\"-f\")\n override fun run() {\n echo(\"$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.
You can specify a limit for the number of times the counted option can be given, and either clamp the value or show an error if the limit is exceeded.
ExampleUsage
class Log : CliktCommand() {\n val verbosity by option(\"-v\").counted(limit=3, clamp=true)\n override fun run() {\n echo(\"Verbosity level: $verbosity\")\n }\n}\n
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() {\n val size by option().switch(\n \"--large\" to \"large\",\n \"--small\" to \"small\"\n ).default(\"unknown\")\n override fun run() {\n echo(\"You picked size $size\")\n }\n}\n
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 {\n data class Oranges(val size: String): Fruit()\n data class Apples(val count: Int): Fruit()\n}\nclass Order : CliktCommand() {\n val fruit: Fruit? by mutuallyExclusiveOptions<Fruit>(\n option(\"--oranges\").convert { Oranges(it) },\n option(\"--apples\").int().convert { Apples(it) }\n )\n\n override fun run() = echo(fruit)\n}\n
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() {\n val name by option().required()\n val age by option().int()\n}\nclass Tool : CliktCommand() {\n val userOptions by UserOptions().cooccurring()\n override fun run() {\n userOptions?.let {\n echo(it.name)\n echo(it.age)\n } ?: echo(\"No user options\")\n }\n}\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\") {\n val path by option().file().required()\n val followSymlinks by option().flag()\n}\n\nclass FromNetwork: LoadConfig(\"Options for loading from network\") {\n val url by option().required()\n val username by option().prompt()\n val password by option().prompt(hideInput = true)\n}\n\nclass Tool : CliktCommand() {\n val load by option().groupChoice(\n \"disk\" to FromDisk(),\n \"network\" to FromNetwork()\n )\n\n override fun run() {\n when(val it = load) {\n is FromDisk -> echo(\"Loading from disk: ${it.path}\")\n is FromNetwork -> echo(\"Loading from network: ${it.url}\")\n null -> 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=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() {\n val level by option(\"-l\", \"--level\", metavar = \"<number>\")\n .int(acceptsValueWithoutName = true)\n\n override fun run() {\n echo(\"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() {\n val name by option().prompt()\n override fun run() {\n echo(\"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.
Sometimes you want an option to halt execution immediately and print a message. For example, the built-in --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 these options don\u2019t have values, you can\u2019t define them using property delegates. Instead, call the function on a command directly, either in an init block, or on a command instance.
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() {\n init {\n eagerOption(\"--version\") {\n throw PrintMessage(\"$name 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 can\u2019t reference other options or arguments, since they\u2019re evaluated before parsing the rest of the command line. They can be declared in regular OptionGroups, but not in other types of groups like switch groups.
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() {\n val opt by option(help = \"option 1\").deprecated()\n val opt2 by option(help = \"option 2\").deprecated(\"WARNING: --opt2 is deprecated, use --new-opt instead\", tagName = null)\n val opt3 by option(help = \"option 3\").deprecated(tagName = \"pending deprecation\", tagValue = \"use --new-opt instead\")\n val opt4 by option(help = \"option 4\").deprecated(error = true)\n\n override 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
You may want to collect unknown options for manual processing. You can do this by overriding treatUnknownOptionsAsArgs = true in your command. 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() {\n init { context { allowInterspersedArgs = false }}\n override val treatUnknownOptionsAsArgs = true\n\n val command by option().required()\n val arguments by argument().multiple()\n\n override fun run() {\n val cmd = (listOf(command) + arguments).joinToString(\" \")\n val proc = Runtime.getRuntime().exec(cmd)\n echo(proc.inputStream.bufferedReader().readText())\n proc.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() {\n val name by option(envvar = \"MY_NAME\")\n override fun run() {\n echo(\"Hello $name\")\n }\n}\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() {\n init {\n context { autoEnvvarPrefix = \"HELLO\" }\n }\n val name by option()\n override fun run() {\n echo(\"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.readEnvvar.
@Test\nfun `test envvar`() {\n val envvars = mapOf(\"MY_TOOL_OPTION\" to \"value\")\n val tool = MyTool().context {\n readEnvvar = { envvars[it] }\n }\n tool.parse(emptyList())\n assertEquals(\"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() {\n init {\n context {\n valueSource = PropertiesValueSource.from(\"myconfig.properties\")\n }\n }\n val name by option()\n override fun run() {\n echo(\"Hello $name\")\n }\n}\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() {\n val name by option(\"/name\", help=\"your name\")\n override fun run() {\n echo(\"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() {\n val name by option(\"-name\", help=\"your name\")\n override fun run() {\n echo(\"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.
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
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.
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.
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() {\n val inferredOpt by option()\n val inferred by argument()\n val explicitOpt by option(\"-e\", \"--explicit\")\n val explicitArg by argument(\"<explicit>\")\n override 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
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().
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.
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.
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 OutputStreams","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.
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() {\n val opt by option().convert { it.toBigDecimal() }\n override 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
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() {\n val opt by option(help=\"a real number\").convert(\"float\") {\n it.toBigDecimalOrNull() ?: fail(\"A real number is required\")\n }\n override 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
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() {\n val file: String by argument()\n .file(mustExist=true, canBeDir=false)\n .convert { it.readText() }\n override fun run() {\n echo(\"Your file contents: $file\")\n }\n}\n
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).
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() {\n val number by option(help = \"An even number\").int()\n .check(\"value must be even\") { it % 2 == 0 }\n\n override fun run() {\n echo(\"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
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() {\n val number by option().int().default(0)\n val biggerNumber by option().int().validate {\n require(it > number) {\n \"--bigger-number must be bigger than --number\"\n }\n }\n\n override fun run() {\n echo(\"number=$number, biggerNumber=$biggerNumber\")\n }\n}\n
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:
$ ./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/#coroutine-commands-with-suspend-functions","title":"Coroutine commands with suspend functions","text":"
If you want to use coroutines in your command, you can use a SuspendingCliktCommand:
class Hello : SuspendingCliktCommand() {\n val count by option(help=\"Number of greetings\").int().default(1)\n val name by argument()\n\n override suspend fun run() {\n for (i in 1..count) {\n echo(\"Hello $name!\")\n delay(1000)\n }\n }\n}\n\nsuspend fun main(args: Array<String>) = Hello().main(args)\n
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\") {\n override fun run() = Unit\n}\n\nclass Init : CliktCommand() {\n override fun help(context: Context) = \"Initialize the database\"\n override fun run() {\n echo(\"Initialized the database.\")\n }\n}\n\nclass Drop : CliktCommand() {\n override fun help(context: Context) = \"Drop the database\"\n override fun run() {\n echo(\"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
To add parameters, use the option and argument property delegates:
ExampleHelp Output
class Hello : CliktCommand() {\n val count by option(help=\"Number of greetings\").int().default(1)\n val name by argument()\n\n override fun run() {\n for (i in 1..count) {\n echo(\"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.
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.
Clikt includes the test extension to help testing commands and their output.
TestCommand
@Test\nfun testHello() {\n val command = Hello()\n val result = command.test(\"--name Foo\")\n assertEqual(result.stdout, \"Hello, Foo!\")\n assertEqual(result.exitCode, 0)\n assertEqual(command.name, \"Foo\")\n}\n
class Hello: CliktCommand() {\n val name by option()\n override fun run() {\n echo(\"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.
You can set environment variables for your command by passing in a map of envvars.
TestCommand
@Test\nfun testHello() {\n val command = Hello()\n val result = command.test(\"\", envvars=mapOf(\"HELLO_NAME\" to \"Foo\"))\n assertEqual(result.stdout, \"Hello, Foo!\")\n}\n
class Hello: CliktCommand() {\n val name by option(envvar=\"HELLO_NAME\")\n override fun run() {\n echo(\"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.
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() {\n val command = Adder()\n val result = command.test(\"\", stdin = \"2\\n3\")\n assertEqual(result.stdout, \"first: second: result: 2 + 3 = 5\")\n}\n
class Adder : TestCommand() {\n val first by option().prompt()\n val second by option().prompt()\n\n override fun run_() {\n echo(\"result: $first + $second = ${first + second}\")\n }\n}\n
CliktCommand.main calls Context.exitProcess to exit the process. You can set that to an empty lambda to skip it, or one that captures the status value if you want to check it in you tests.
"},{"location":"testing/#using-parse-instead-of-main","title":"Using parse instead of main","text":"
Instead of calling main, you can 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.
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\u2019s 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) {\n val v: Boolean by parser.flagging(help=\"enable verbose mode\")\n val username: String? by parser.storing(help=\"name of the user\")\n val count: Int? by parser.storing(help=\"number of the widgets\") { toInt() }\n val source: List<String> by parser.positionalList(help=\"source filenames\")\n val destination: String by parser.positional(help=\"destination\")\n}\n\nfun main(args: Array<String>) = mainBody {\n ArgParser(args).parseInto(::MyArgs).run {\n println(\"Hello, $username!\")\n println(\"Moving $count widgets from $source to $destination.\")\n }\n}\n
Here\u2019s the same thing with Clikt:
class Cli : CliktCommand() {\n val v: Boolean by option(help = \"enable verbose mode\").flag()\n val username: String? by option(help = \"name of the user\")\n val count: Int? by option(help = \"number of the widgets\").int()\n val source: List<String> by argument(help = \"source filenames\").multiple()\n val destination: String by argument(help = \"destination\")\n override fun run() {\n println(\"Hello, $name!\")\n println(\"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) =\n option<MutableMap<String, String>>(*names,\n argNames = listOf(\"KEY\", \"VALUE\"),\n help = help) {\n value.orElse { mutableMapOf<String, String>() }.apply {\n put(arguments.first(), arguments.last()) }\n }\n\n fun ArgParser.putting(help: String) =\n ArgParser.DelegateProvider { identifier ->\n putting(identifierToOptionName(identifier), help = help) }\n\nclass MyArgs(parser: ArgParser) {\n val 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() {\n val 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 82af0560..5723fe7c 100755
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ
diff --git a/testing/index.html b/testing/index.html
index b43e2937..2e50f511 100755
--- a/testing/index.html
+++ b/testing/index.html
@@ -16,7 +16,7 @@
-
+
@@ -2231,7 +2231,7 @@
- Utilities
+ Shell Completion
@@ -2240,111 +2240,6 @@
-
- Utilities
-
-
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.
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
-
-
-
fungetCommitMessage():String?{
-valmessage="""
- # Enter your message.
- # Lines starting with # are ignored
- """.trimIndent()
-returneditText(message,requireSave=true)
-?.replace(Regex("#[^\n]*\n"),"")
-}
-
Options can prompt for values automatically, but you can also do so manually
-by using Mordant’s 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
-
-
-
valinput=terminal.prompt("Enter a number"){
-it.toIntOrNull()
-?.let{ConversionResult.Valid(it)}
-?:ConversionResult.Invalid("$it is not a valid integer")
-}
-echo("Twice your number is ${input*2}")
-
-
-
-
Enter a number: foo
-Error: foo is not a valid integer
-Enter a number: 11
-Twice your number is 22
-