从字符串中解析命令行开关和参数的惯用 clojure
Idiomatic clojure to parse command line switches and args from string
我想解析命令行字符串,并将所有命令开关及其后续参数分组。所以,例如:
(parse "git branch -d f1 f2 -a -m message") =>
[["-d" "f1" "f2"]["-a"]["-m" "message"]]
我忽略了没有立即切换的参数。
我为此编写的代码如下:
(defn switch? [s] (re-find #"\-+" s))
(defn tokenify [s] (clojure.string/split s #" "))
(defn parse [cmd-str]
(loop [lst (tokenify cmd-str), acc [], _acc []]
(let [fs (first lst), rs (rest lst), new? (empty? _acc)]
(cond (empty? lst) (if new? acc (conj acc _acc))
(switch? fs) (if new?
(recur rs acc (conj _acc fs))
(recur rs (conj acc _acc) (conj [] fs)))
:else (if new?
(recur rs acc _acc)
(recur rs acc (conj _acc fs)))))))
这行得通,但它的级别很低而且很糟糕。有没有一种使用 reduce 或 partition 或 group-by 的简单方法可以使相同的功能更简洁、更惯用?
这是使用 clojure.tools.cli
的基本概述
(def command-line-spec
[["-m" "--mode 0|1" "description of option that takes 1 or 0"
:parse-fn #(case (s/lower-case %)
("1" "true" "create") :one
("0" "false" "destroy") :zero
:invalid)
:default :one
:validate [#{:one :zero} "Unsupported mode"]]
["-c" "--config type1|type2|..."
:desc "config specifies what to do"
:default :dostuff
:parse-fn #(if (keyword? %)
%
(-> % s/lower-case keyword))
:validate-fn #(contains? configurations %)]
["-n" "--name service Name"
:default (getenv "NAME")]
[nil "--min number"
:default 7 :parse-fn #(Integer/parseInt %)]
[nil "--max number"
:default 7 :parse-fn #(Integer/parseInt %)]
[nil "--public true|false" "true or false"
:default false
:parse-fn #(Boolean/parseBoolean %)]
["-h" "--help"]])
(defn -main [& args]
(let [{:keys [options arguments errors summary]}
(parse-opts args
command-line-spec)
... )
我想解析命令行字符串,并将所有命令开关及其后续参数分组。所以,例如:
(parse "git branch -d f1 f2 -a -m message") =>
[["-d" "f1" "f2"]["-a"]["-m" "message"]]
我忽略了没有立即切换的参数。
我为此编写的代码如下:
(defn switch? [s] (re-find #"\-+" s))
(defn tokenify [s] (clojure.string/split s #" "))
(defn parse [cmd-str]
(loop [lst (tokenify cmd-str), acc [], _acc []]
(let [fs (first lst), rs (rest lst), new? (empty? _acc)]
(cond (empty? lst) (if new? acc (conj acc _acc))
(switch? fs) (if new?
(recur rs acc (conj _acc fs))
(recur rs (conj acc _acc) (conj [] fs)))
:else (if new?
(recur rs acc _acc)
(recur rs acc (conj _acc fs)))))))
这行得通,但它的级别很低而且很糟糕。有没有一种使用 reduce 或 partition 或 group-by 的简单方法可以使相同的功能更简洁、更惯用?
这是使用 clojure.tools.cli
的基本概述(def command-line-spec
[["-m" "--mode 0|1" "description of option that takes 1 or 0"
:parse-fn #(case (s/lower-case %)
("1" "true" "create") :one
("0" "false" "destroy") :zero
:invalid)
:default :one
:validate [#{:one :zero} "Unsupported mode"]]
["-c" "--config type1|type2|..."
:desc "config specifies what to do"
:default :dostuff
:parse-fn #(if (keyword? %)
%
(-> % s/lower-case keyword))
:validate-fn #(contains? configurations %)]
["-n" "--name service Name"
:default (getenv "NAME")]
[nil "--min number"
:default 7 :parse-fn #(Integer/parseInt %)]
[nil "--max number"
:default 7 :parse-fn #(Integer/parseInt %)]
[nil "--public true|false" "true or false"
:default false
:parse-fn #(Boolean/parseBoolean %)]
["-h" "--help"]])
(defn -main [& args]
(let [{:keys [options arguments errors summary]}
(parse-opts args
command-line-spec)
... )