在Clojure中构建CLI脚本

时间:2018-05-19 18:59:07

标签: clojure scripting

在Clojure中构建CLI脚本的常用/标准方法是什么?

在我看来,这种方法应该包括以下特征:

  • 一种轻松处理参数的方法,stdin / out / err。

  • 不需要花太多时间来启动(理想情况下有某种JIT),否则就会失去在一个人的外壳中将事情混在一起的目的。

  • 此外,在没有设置项目的情况下(可能在全局范围内安装它们)期望一种简单的方法来包含一次性依赖项是合理的。

理想情况下,非常感谢提供解决方案使用的简单示例。有点相当于:

#!/bin/bash
    echo "$@"
    cat /dev/stdin

注意:我知道这个问题之前有点质疑here。但问题是不完整的,答案似乎没有达成共识,也没有达成共识的大部分解决方案。

5 个答案:

答案 0 :(得分:3)

选项是Planck,它在MacOS和Linux上运行。它使用自托管的ClojureScript,具有快速启动功能,并以JavaScriptCore为目标。

它有一个很好的SDK并模仿Clojure中的一些你在ClojureScript中没有的东西,例如: ID DATES RATES 1 2014-07-01 0.02 1 2014-07-02 0.03 1 2014-07-03 0.04 1 2014-07-04 0.05 2 2014-07-01 0.02 2 2014-07-02 0.03 2 2014-07-03 0.04 2 2014-07-04 0.06 3 2014-07-02 0.03 3 2014-07-03 0.04 3 2014-07-04 0.05 类似于planck.io。它支持通过clojure.java.io / tools.deps.alpha加载依赖项。

回应deps.edn就像以下一样简单:

stdin

并打印命令行参数:

(require '[planck.core :refer [*in* slurp]])
(print (slurp *in*))

...

(println *command-line-args*)

具有依赖关系的独立脚本(即非项目)的示例:the tree command line tool in Planck

有一点需要注意的是,普朗克并不支持使用npm依赖项。因此,如果您需要这些,请转到针对NodeJS的Lumo

第三个选项是joker,它是用Go编写的Clojure解释器。

答案 1 :(得分:3)

现在有new CLI tooling,可以在不使用第三方工具的情况下创建独立的Clojure脚本。一旦有了clj命令行工具installed,下面的脚本便应该可以正常工作。

就原始问题而言,这取决于任何libraries:require来处理命令行参数和系统输入/输出,与任何Clojure / JVM CLI程序一样好。我还没有对它进行基准测试,因此我不会对性能进行评论,但是如果它让您感到担心,请尝试一下一下,看看启动时间是否适合您。但我会说这在依赖项管理上得分很高,因为该脚本是完全独立的(除了clj工具之外,该工具现在无论如何都是推荐的运行Clojure的方法)。

文件:~/bin/script.sh

#!/bin/sh

"exec" "clj" "-Sdeps" "{:deps,{hiccup,{:mvn/version,\"1.0.5\"}}}" "$0" "$@"

(ns my-script
  (:require
    [hiccup.core :as hiccup]))

(println
  (hiccup/html
    [:div
      [:span "Command line args: " (clojure.string/join ", " *command-line-args*)]
      [:span "Stdin: " (read-line)]]))

然后确保其可执行:

$ chmod +x ~/bin/script.sh

并运行它:

$ echo "stdin" | script.sh command line args
<div><span>Command line args: command, line, args</span><span>Stdin: stdin</span></div>

NB。这主要是一个shell脚本,它将第三行上的字符串视为要执行的命令。随后的执行将使用给定的参数运行clj命令行工具,该工具将把这些字符串评估为字符串(无副作用),然后继续评估下面的Clojure代码。

还要注意,依赖关系被指定为第三行传递给clj的映射。您可以阅读有关on the Clojure website的工作原理的更多信息。依存关系图中的标记由逗号分隔,Clojure将其视为空格,但大多数shell则不会。

感谢“ clojurians” Slack小组的#tools-deps频道上的好人,这个解决方案来了。

答案 2 :(得分:2)

我知道你要求非项目创建方法来实现这个目标,但由于这个特定的问题已经在我脑海中浮现了很长一段时间,我想我会投入另一种选择。

TLDR:跳转到下面的“创建可执行CLI命令”部分

背景

我有一些相同的要求列表,就像你回过头来创建可执行jar文件一样。我不是通过java -jar myfile.jar讨论可执行文件,而是直接执行自包含的uber-jar,就像使用任何其他二进制文件一样。

如果您阅读zip file specification(jar文件作为jar文件是一个zip文件),事实证明这实际上是可行的。简短版本是您需要:

  • 用你需要的东西建造一个胖罐子
  • 将bash / bat / shell脚本插入文件开头的二进制jar内容
  • chmod +x超级jar文件(或者如果在Windows上,请检查可执行文件框)
  • 重写jar文件元数据记录,以便插入的脚本文本不会使zip文件内部偏移无效

应该注意,这实际上是由zip文件规范支持的。这就是自解压zip文件等的工作原理以及生成的胖jar(在上面的过程之后)仍然是一个有效的jar文件和一个有效的zip存档。所有相关命令(例如java -jar)仍然有效,文件现在也可以直接从命令行执行。

此外,遵循上述模式,还可以添加对drip jvm launcher等内容的支持,从而大大加快cli脚本的启动时间。

事实证明,大约一年前我开始研究这个问题时,最后一个重写jar文件元数据的库并不存在。不仅仅是在clojure中,而是在整个JVM上。这仍然让我大吃一惊:jvm上所有语言的中央部署单元都是jar文件,那里没有实际读取jar文件内部的库。实际的zip文件结构中的内部结构,而不仅仅是java的ZipFile和朋友的内容。

此外,我找不到一个用于clojure的库,它处理了zip文件规范以干净的方式所需的二进制结构。

解决方案:

  • octet有我认为可用的clojure二进制库最干净的接口,所以我写了一个pull request for octet添加对zip文件规范所需功能的支持。
  • 然后我创建了一个新的库clj-zip-meta,它读取并解释了zip文件元数据,并且能够进行上面最后一点中描述的偏移重写。
  • 然后我创建了pull request到现有的clojure库lein-binplus,以添加对clj-zip-meta实现的zip元重写的支持,并添加对自定义前导脚本的支持,以便能够创建真正的可执行jar,无需java -jar
  • 毕竟我创建了一个leiningen模板cli-cmd来支持创建支持所有上述铃声和口哨的cli命令项目,并且具有结构良好的命令行解析设置...或者我认为结构良好的:) 。欢迎评论。

创建可执行CLI命令

所以,您可以使用leiningen创建一个新的命令行clojure应用程序并使用以下命令运行它:

~> lein new cli-cmd mycmd

~> cd mycmd

~> lein bin 

Compiling mycmd.core
Compiling mycmd.core
Created /home/mbjarland/tmp/clj-cmd/mycmd/target/mycmd-0.1.0-SNAPSHOT.jar
Created /home/mbjarland/tmp/clj-cmd/mycmd/target/mycmd-0.1.0-SNAPSHOT-standalone.jar
Creating standalone executable: /home/mbjarland/tmp/clj-cmd/mycmd/target/mycmd
Re-aligning zip offsets

~> target/mycmd 
---- debug output, remove for production code ----
options    {:port 80, :hostname "localhost", :verbosity 0}
arguments  []
errors     nil
summary    
   -p, --port PORT      80         Port number
  -H, --hostname HOST  localhost  Remote host
      --detach                    Detach from controlling process
  -v                              Verbosity level; may be specified multiple times to increase value
  -h, --help
--------------------------------------------------
This is my program. There are many like it, but this one is mine.

Usage: mycmd [options] action

Options:
  -p, --port PORT      80         Port number
  -H, --hostname HOST  localhost  Remote host
      --detach                    Detach from controlling process
  -v                              Verbosity level; may be specified multiple times to increase value
  -h, --help

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.

Error: invalid action '' specified!   

命令的输出只是样板命令行解析,我已经添加到leiningen模板中。

自定义序言脚本位于boot/jar-preamble.sh,它支持滴水。换句话说,如果你的路径上有滴水,生成的可执行文件将使用它,否则它将回退到内部启动超级jar的标准java -jar方式。

命令行解析的源代码和cli app的代码按照正常情况存在于src目录下。

如果您想要黑客攻击,可以更改前导脚本并重新运行lein bin,新的前导码将通过构建过程插入到您的可执行文件中。

另外需要注意的是,此方法仍然会java -jar,因此您 需要在您的路径上使用java。

Ayway,啰嗦的解释,但希望对于有这个问题的人来说会有一些用处。

答案 3 :(得分:1)

考虑Lumo,一个专门为脚本设计的ClojureScript环境。

请注意,虽然它支持ClojureScript(JAR)和NPM依赖项,但依赖性支持为still under development

答案 4 :(得分:0)

我编写了许多Clojure(JVM)脚本,并使用CLI-matic库https://github.com/l3nz/cli-matic/提取了大多数与命令行解析,帮助的创建和维护,错误等相关的样板文件。