• Stars
    star
    711
  • Rank 63,628 (Top 2 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created over 13 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Command-line processing

tools.cli

Tools for working with command line arguments.

Stable Releases and Dependency Information

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.0.219

clj/deps.edn dependency information:

org.clojure/tools.cli {:mvn/version "1.0.219"}

Leiningen dependency information:

[org.clojure/tools.cli "1.0.219"]

Maven dependency information:

<dependency>
  <groupId>org.clojure</groupId>
  <artifactId>tools.cli</artifactId>
  <version>1.0.219</version>
 </dependency>

The 0.4.x series of tools.cli supports use with clj/deps.edn and brings the legacy API to ClojureScript by switching to .cljc files. This means it requires Clojure(Script) 1.8 or later.

The 0.3.x series of tools.cli features a new flexible API, better adherence to GNU option parsing conventions, and ClojureScript support.

The function clojure.tools.cli/cli has been superseded by clojure.tools.cli/parse-opts, and should not be used in new programs.

The previous function will remain for the foreseeable future. It has also been adapted to use the new tokenizer, so upgrading is still worthwhile even if you are not ready to migrate to parse-opts.

Quick Start

(ns my.program
  (:require [clojure.tools.cli :refer [parse-opts]])
  (:gen-class))

(def cli-options
  ;; An option with a required argument
  [["-p" "--port PORT" "Port number"
    :default 80
    :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
    :default 0
    :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 use lein run or however you run your program instead of clojure -M -m my.program)

to produce the map:

{:options   {:port 8080
             :verbosity 3
             :help true}

 :arguments ["foo"]

 :summary   "  -p, --port PORT  80  Port number
               -v                   Verbosity level
               -h, --help"

 :errors    ["Unknown option: \"--invalid-opt\""]}

Note that exceptions are not thrown on parse errors, so errors must be handled explicitly after checking the :errors entry for a truthy value.

Please see the example program for a more detailed example and refer to the docstring of parse-opts for comprehensive documentation:

http://clojure.github.io/tools.cli/index.html#clojure.tools.cli/parse-opts

See Also

An interesting library built on top of tool.cli that provides a more compact, higher-level API is cli-matic.

Since Release 0.3.x

Better Option Tokenization

In accordance with the GNU Program Argument Syntax Conventions, two features have been added to the options tokenizer:

  • Short options may be grouped together.

    For instance, -abc is equivalent to -a -b -c. If the -b option requires an argument, the same -abc is interpreted as -a -b "c".

  • Long option arguments may be specified with an equals sign.

    --long-opt=ARG is equivalent to --long-opt "ARG".

    If the argument is omitted, it is interpreted as the empty string. e.g. --long-opt= is equivalent to --long-opt ""

In-order Processing for Subcommands

Large programs are often divided into subcommands with their own sets of options. To aid in designing such programs, clojure.tools.cli/parse-opts accepts an :in-order option that directs it to stop processing arguments at the first unrecognized token.

For instance, the git program has a set of top-level options that are unrecognized by subcommands and vice-versa:

git --git-dir=/other/proj/.git log --oneline --graph

By default, clojure.tools.cli/parse-opts interprets this command line as:

options:   [[--git-dir /other/proj/.git]
            [--oneline]
            [--graph]]
arguments: [log]

When :in-order is true however, the arguments are interpreted as:

options:   [[--git-dir /other/proj/.git]]
arguments: [log --oneline --graph]

Note that the options to log are not parsed, but remain in the unprocessed arguments vector. These options could be handled by another call to parse-opts from within the function that handles the log subcommand.

Options Summary

parse-opts returns a minimal options summary string:

  -p, --port NUMBER  8080       Required option with default
      --host HOST    localhost  Short and long options may be omitted
  -d, --detach                  Boolean option
  -h, --help

This may be inserted into a larger usage summary, but it is up to the caller.

If the default formatting of the summary is unsatisfactory, a :summary-fn may be supplied to parse-opts. This function will be passed the sequence of compiled option specification maps and is expected to return an options summary.

The default summary function clojure.tools.cli/summarize is public and may be useful within your own :summary-fn for generating the default summary.

Option Argument Validation

By default, option validation is performed immediately after parsing, which means that "flag" arguments will have a Boolean value, even if a :default is specified with a different type of value.

You can choose to perform validation after option processing instead, with the :post-validation true flag. During option processing, :default values are applied and :assoc-fn and :update-fn are invoked. If an option is specified more than once, :post-validation true will cause validation to be performed after each new option value is processed.

There is a new option entry :validate, which takes a tuple of [validation-fn validation-msg]. The validation-fn receives an option's argument after being parsed by :parse-fn if it exists. The validation-msg can either be a string or a function of one argument that can be called on the invalid option argument to produce a string:

["-p" "--port PORT" "A port number"
 :parse-fn #(Integer/parseInt %)
 :validate [#(< 0 % 0x10000) #(str % " is not a number between 0 and 65536")]]

If the validation-fn returns a falsey value, the validation-msg is added to the errors vector.

Error Handling and Return Values

Instead of throwing errors, parse-opts collects error messages into a vector and returns them to the caller. Unknown options, missing required arguments, validation errors, and exceptions thrown during :parse-fn are all added to the errors vector.

Any option can be flagged as required by providing a :missing key in the option spec with a string that should be used for the error message if the option is omitted.

The error message when a required argument is omitted (either a short opt with :require or a long opt describing an argument) is:

Missing required argument for ...

Correspondingly, parse-opts returns the following map of values:

{:options     A map of default options merged with parsed values from the command line
 :arguments   A vector of unprocessed arguments
 :summary     An options summary string
 :errors      A vector of error messages, or nil if no errors}

During development, parse-opts asserts the uniqueness of option :id, :short-opt, and :long-opt values and throws an error on failure.

ClojureScript Support

As of 0.4.x, the namespace is clojure.tools.cli for both Clojure and ClojureScript programs. The entire API, including the legacy (pre-0.3.x) functions, is now available in both Clojure and ClojureScript.

For the 0.3.x releases, the ClojureScript namespace was cljs.tools.cli and only parse-opts and summarize were available.

Example Usage

(ns cli-example.core
  (:require [cli-example.server :as server]
            [clojure.string :as string]
            [clojure.tools.cli :refer [parse-opts]])
  (:import (java.net InetAddress))
  (:gen-class))

(def cli-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"
    :default 80
    :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
    :default 0
    ;; Use :update-fn to create non-idempotent options (:default is applied first)
    :update-fn inc]
   ["-f" "--file NAME" "File names to read"
    :multi true ; 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]
   ;; A boolean option that can explicitly be set to false
   ["-d" "--[no-]daemon" "Daemonize the process" :default true]
   ["-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)

(defn usage [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)))

(defn error-msg [errors]
  (str "The following errors occurred while parsing your command:\n\n"
       (string/join \newline errors)))

(defn validate-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)})))

(defn exit [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? 0 1) exit-message)
      (case action
        "start"  (server/start! options)
        "stop"   (server/stop! options)
        "status" (server/status! options)))))

Developer Information

License

Copyright (c) Rich Hickey and contributors. All rights reserved.

The use and distribution terms for this software are covered by the Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 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 by the terms of this license.

You must not remove this notice, or any other, from this software.

More Repositories

1

clojure

The Clojure programming language
Java
10,334
star
2

clojurescript

Clojure to JS compiler
Clojure
9,191
star
3

core.async

Facilities for async programming and communication in Clojure
Clojure
1,935
star
4

clojure-clr

A port of Clojure to the CLR, part of the Clojure project
C#
1,541
star
5

core.logic

A logic programming library for Clojure & ClojureScript
Clojure
1,434
star
6

core.typed

An optional type system for Clojure
Clojure
1,285
star
7

core.match

An optimized pattern matching library for Clojure
Clojure
1,180
star
8

test.check

QuickCheck for Clojure
Clojure
1,112
star
9

java.jdbc

JDBC from Clojure (formerly clojure.contrib.sql)
Clojure
714
star
10

tools.nrepl

A Clojure network REPL that provides a server and client, along with some common APIs of use to IDEs and other tools that may need to evaluate Clojure code in remote environments.
Clojure
661
star
11

tools.namespace

Tools for managing namespaces in Clojure
Clojure
596
star
12

data.json

JSON in Clojure
Clojure
536
star
13

algo.monads

Macros for defining monads, and definition of the most common monads
Clojure
444
star
14

core.cache

A caching library for Clojure implementing various cache strategies
Clojure
442
star
15

tools.deps.alpha

A functional API for transitive dependency graph expansion and the creation of classpaths
Clojure
435
star
16

tools.logging

Clojure logging API
Clojure
382
star
17

tools.trace

1.3 update of clojure.contrib.trace
Clojure
354
star
18

math.combinatorics

Efficient, functional algorithms for generating lazy sequences for common combinatorial functions
Clojure
343
star
19

spec-alpha2

Clojure library to describe the structure of data and functions
Clojure
297
star
20

data.csv

CSV reader/writer to/from Clojure data structures
Clojure
270
star
21

core.memoize

A manipulable, pluggable, memoization framework for Clojure
Clojure
263
star
22

tools.analyzer

An analyzer for Clojure code, written in Clojure and producing AST in EDN
Clojure
257
star
23

clojure-site

clojure.org site
HTML
249
star
24

data.xml

Clojure
220
star
25

data.finger-tree

Finger Tree data structure
Clojure
213
star
26

spec.alpha

Clojure library to describe the structure of data and functions
Clojure
212
star
27

tools.reader

Clojure reader in Clojure
Clojure
203
star
28

tools.build

Clojure builds as Clojure programs
Clojure
200
star
29

core.rrb-vector

RRB-Trees in Clojure
Clojure
191
star
30

data.priority-map

Clojure priority map data structure
Clojure
186
star
31

math.numeric-tower

Math functions that deal intelligently with the various types in Clojure's numeric tower
Clojure
175
star
32

test.generative

Generative test runner
Clojure
161
star
33

core.unify

Unification library
Clojure
137
star
34

core.contracts

Contracts programming
Clojure
127
star
35

data.fressian

Read and write Fressian data from Clojure
Clojure
127
star
36

data.avl

Persistent sorted maps and sets with log-time rank queries
Clojure
125
star
37

data.int-map

A map optimized for integer keys
Java
124
star
38

core.incubator

Proving ground for proposed new core fns
Clojure
116
star
39

java.data

Functions for recursively converting Java beans to Clojure and vice versa
Clojure
114
star
40

tools.analyzer.jvm

Additional jvm-specific passes for tools.analyzer
Clojure
113
star
41

tools.macro

Utilities for macro writers
Clojure
113
star
42

clojurescript-site

website for ClojureScript
Shell
106
star
43

tools.deps.graph

Dependency graphs for deps.edn projects
Clojure
106
star
44

java.jmx

Produce and consume JMX beans from Clojure
Clojure
94
star
45

algo.generic

Generic versions of commonly used functions, implemented as multimethods that can be implemented for any data type
Clojure
92
star
46

tools.emitter.jvm

A JVM bytecode generator for ASTs compatible with tools.analyzer(.jvm)
Clojure
86
star
47

data.generators

Random data generators
Clojure
85
star
48

data.zip

Utilities for clojure.zip
Clojure
83
star
49

brew-install

Clojure CLI installer
Shell
81
star
50

data.codec

Native codec implementations
Clojure
74
star
51

tools.gitlibs

API for retrieving, caching, and programatically accessing git libraries
Clojure
62
star
52

java.classpath

Examine the Java classpath from Clojure programs
Clojure
59
star
53

jvm.tools.analyzer

Clojure
53
star
54

core.specs.alpha

specs to describe Clojure core macros and functions
Clojure
47
star
55

tools.tools

Clojure CLI tool for managing Clojure CLI tools
Clojure
42
star
56

homebrew-tools

Clojure homebrew tap providing Clojure formulae
Ruby
41
star
57

data.alpha.replicant-server

A Clojure library providing remote implementations of the Clojure data structures and a remote REPL server.
Clojure
37
star
58

test.benchmark

Benchmark and Regression Suite for Clojure
Roff
37
star
59

clr.tools.nrepl

Clojure
25
star
60

build.ci

Support scripts for continuous integration
Clojure
23
star
61

tools.analyzer.js

Provides js-specific passes for tools.analyzer
Clojure
21
star
62

algo.graph

Basic graph theory algorithms
Clojure
16
star
63

clojure-install

Java
16
star
64

data.alpha.replicant-client

A Clojure library providing client-side implementations of Clojure datastructures served by replicant-server.
Clojure
13
star
65

clojure.github.com

Documentation repos
HTML
8
star
66

build.poms

Parent POMs
8
star
67

core.typed.analyzer.jvm

Clojure
7
star
68

clr.tools.namespace

Clojure
7
star
69

core.typed.runtime.jvm

Clojure
7
star
70

clr.data.json

JSON in Clojure on the CLR
Clojure
6
star
71

clr.tools.reader

Clojure
5
star
72

clr.test.generative

Clojure
5
star
73

clojure-api-doc

Clojure API doc build
Clojure
5
star
74

contrib-api-doc

Clojure contrib API doc build
Clojure
5
star
75

core.typed.annotator.jvm

Clojure
5
star
76

core.typed.checker.jvm

Clojure
4
star
77

core.typed.checker.js

Clojure
4
star
78

io.incubator

Proving ground for proposed new io fns
4
star
79

clr.data.generators

Random data generators for Clojure on the CLR
Clojure
4
star
80

clr.core.async

Port of Clojure core.async to the CLR
Clojure
3
star
81

clr.spec.alpha

spec on the CLR
Clojure
3
star
82

clr.tools.analyzer

Clojure
3
star
83

test.regression

Regression tests for Clojure
Clojure
3
star
84

tools.deps.cli

Deps functions
Clojure
2
star
85

clr.core.specs.alpha

core specs on CLR
HTML
2
star
86

java.internal.invoke

2
star
87

clr.tools.gitlibs

An API for retrieving, caching, and programatically accessing git libraries
HTML
2
star
88

clr.core.logic

Clojure
2
star
89

clr.tools.trace

1
star
90

clr.core.cli

Clojure
1
star
91

clr.data.priority-map

ClojureCLR port of data.priority-map
Clojure
1
star
92

cljs.tools.closure

ClojureScript build of Google Closure
Shell
1
star
93

tools.analyzer.clr

additional clr-specific passes for tools.analyzer
Clojure
1
star
94

clr.test.check

Clojure
1
star
95

clr.core.cache

ClojureCLR port of core.cache
Clojure
1
star
96

clr.tools.logging

1
star
97

build.test

Dummy project for testing contrib build and deploy
Clojure
1
star
98

clr.core.memoize

ClojureCLR port of core.memoize
Clojure
1
star