- Notifications
You must be signed in to change notification settings - Fork52
clojure/tools.cli
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Tools for working with command line arguments.
This project follows the version scheme MAJOR.MINOR.COMMITS where MAJOR and MINOR provide some relative indication of the size of the change, but do not follow semantic versioning. In general, all changes endeavor to be non-breaking (by moving to new names rather than by breaking existing names). COMMITS is an ever-increasing counter of commits since the beginning of this repository.
Latest stable release: 1.1.230
clj/deps.edn dependency information:
org.clojure/tools.cli {:mvn/version"1.1.230"}
Leiningen dependency information:
[org.clojure/tools.cli"1.1.230"]
Maven dependency information:
<dependency> <groupId>org.clojure</groupId> <artifactId>tools.cli</artifactId> <version>1.1.230</version> </dependency>
Starting with 0.4.x,tools.cli
supports use withclj
/deps.edn
and bringsthe legacy API to ClojureScript by switching to.cljc
files. This means itrequires Clojure(Script) 1.9 or later.
The 0.3.x series of tools.cli introduced a new flexible API, better adherenceto GNU option parsing conventions, and ClojureScript support.
The old functionclojure.tools.cli/cli
was superseded byclojure.tools.cli/parse-opts
, and should not be used in new programs.
The older function will remain for the foreseeable future. It has also beenadapted to use the new tokenizer, so upgrading is still worthwhile even if youare not ready to migrate toparse-opts
.
(nsmy.program (:require [clojure.tools.cli:refer [parse-opts]]) (:gen-class))(defcli-options;; An option with a required argument [["-p""--port PORT""Port number":default80:parse-fn #(Integer/parseInt %):validate [#(<0 %0x10000)"Must be a number between 0 and 65536"]];; A non-idempotent option (:default is applied first) ["-v"nil"Verbosity level":id:verbosity:default0:update-fn inc]; Prior to 0.4.1, you would have to use:;; :assoc-fn (fn [m k _] (update-in m [k] inc));; A boolean option defaulting to nil ["-h""--help"]])(defn-main [& args] (parse-opts args cli-options))
Execute the command line:
clojure -M -m my.program -vvvp8080 foo --help --invalid-opt
(or uselein run
or however you run your program instead ofclojure -M -m my.program
)
to produce the map:
{:options {:port8080:verbosity3:helptrue}:arguments ["foo"]:summary" -p, --port PORT 80 Port number -v Verbosity level -h, --help":errors ["Unknown option:\"--invalid-opt\""]}
Note that exceptions arenot thrown on parse errors, so errors must behandled explicitly after checking the:errors
entry for a truthy value.
Please see theexample program for a more detailed exampleand refer to the docstring ofparse-opts
for comprehensive documentation(as part of theAPI Documentation):
https://clojure.github.io/tools.cli/index.html#clojure.tools.cli/parse-opts
An interesting library built on top oftool.cli
that provides a more compact,higher-level API iscli-matic.
This is an example of a program that uses most of thetools.cli
features.For detailed documentation, please see the docstring ofparse-opts
.
(nscli-example.core (:require [cli-example.server:as server] [clojure.string:as string] [clojure.tools.cli:refer [parse-opts]]) (:import (java.net InetAddress)) (:gen-class))(defcli-options [;; First three strings describe a short-option, long-option with optional;; example argument description, and a description. All three are optional;; and positional. ["-p""--port PORT""Port number":default80:parse-fn #(Integer/parseInt %):validate [#(<0 %0x10000)"Must be a number between 0 and 65536"]] ["-H""--hostname HOST""Remote host":default (InetAddress/getByName"localhost");; Specify a string to output in the default column in the options summary;; if the default value's string representation is very ugly:default-desc"localhost":parse-fn #(InetAddress/getByName %)];; If no required argument description is given, the option is assumed to;; be a boolean option defaulting to nil [nil"--detach""Detach from controlling process"] ["-v"nil"Verbosity level; may be specified multiple times to increase value";; If no long-option is specified, an option :id must be given:id:verbosity:default0;; Use :update-fn to create non-idempotent options (:default is applied first):update-fn inc] ["-f""--file NAME""File names to read":multitrue; use :update-fn to combine multiple instance of -f/--file:default [];; with :multi true, the :update-fn is passed both the existing parsed;; value(s) and the new parsed value from each option:update-fn conj] ["-t"nil"Timeout in seconds";; Since there is no long option, :id is required...:id:timeout;; ...and we require an argument to be provided::required"TIMEOUT";; parse-long was added in Clojure 1.11::parse-fn parse-long];; A boolean option that can explicitly be set to false ["-d""--[no-]daemon""Daemonize the process":defaulttrue] ["-h""--help"]]);; The :default values are applied first to options. Sometimes you might want;; to apply default values after parsing is complete, or specifically to;; compute a default value based on other option values in the map. For those;; situations, you can use :default-fn to specify a function that is called;; for any options that do not have a value after parsing is complete, and;; which is passed the complete, parsed option map as it's single argument.;; :default-fn (constantly 42) is effectively the same as :default 42 unless;; you have a non-idempotent option (with :update-fn or :assoc-fn) -- in which;; case any :default value is used as the initial option value rather than nil,;; and :default-fn will be called to compute the final option value if none was;; given on the command-line (thus, :default-fn can override :default);; Note: validation is *not* performed on the result of :default-fn (this is;; an open issue for discussion and is not currently considered a bug).(defnusage [options-summary] (->> ["This is my program. There are many like it, but this one is mine.""""Usage: program-name [options] action""""Options:" options-summary"""Actions:"" start Start a new server"" stop Stop an existing server"" status Print a server's status""""Please refer to the manual page for more information."] (string/join \newline)))(defnerror-msg [errors] (str"The following errors occurred while parsing your command:\n\n" (string/join \newline errors)))(defnvalidate-args"Validate command line arguments. Either return a map indicating the program should exit (with an error message, and optional ok status), or a map indicating the action the program should take and the options provided." [args] (let [{:keys [options arguments errors summary]} (parse-opts args cli-options)] (cond (:help options); help => exit OK with usage summary {:exit-message (usage summary):ok?true} errors; errors => exit with description of errors {:exit-message (error-msg errors)};; custom validation on arguments (and (=1 (count arguments)) (#{"start""stop""status"} (first arguments))) {:action (first arguments):options options}:else; failed custom validation => exit with usage summary {:exit-message (usage summary)})))(defnexit [status msg] (println msg) (System/exit status))(defn-main [& args] (let [{:keys [action options exit-message ok?]} (validate-args args)] (if exit-message (exit (if ok?01) exit-message) (case action"start" (server/start! options)"stop" (server/stop! options)"status" (server/status! options)))))
Copyright (c) Rich Hickey and contributors. All rights reserved.
The use and distribution terms for this software are covered by theEclipse Public License 1.0 (https://opensource.org/license/epl-1-0/)which can be found in the file epl.html at the root of this distribution.By using this software in any fashion, you are agreeing to be bound bythe terms of this license.
You must not remove this notice, or any other, from this software.