在Clojure中构建CLI脚本

在Clojure中构建CLI脚本,clojure,scripting,Clojure,Scripting,在Clojure中构建CLI脚本的常用/标准方法是什么 我认为这种方法应包括以下特点: 一种容易处理参数的方法,stdin/out/err 没有太多的启动时间(理想情况下是有某种JIT),否则就失去了在shell中把东西拼凑在一起的目的 此外,我们有理由期待一种简单的方法,可以在不设置项目的情况下包含一次性依赖项(可能是全局安装) 理想情况下,最好提供一个简单的解决方案使用示例。相当于: #!/bin/bash echo "$@" cat /dev/stdin 注意:我知道

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

我认为这种方法应包括以下特点:

  • 一种容易处理参数的方法,stdin/out/err

  • 没有太多的启动时间(理想情况下是有某种JIT),否则就失去了在shell中把东西拼凑在一起的目的

  • 此外,我们有理由期待一种简单的方法,可以在不设置项目的情况下包含一次性依赖项(可能是全局安装)

理想情况下,最好提供一个简单的解决方案使用示例。相当于:

#!/bin/bash
    echo "$@"
    cat /dev/stdin
注意:我知道这个问题以前有人问过。但是这个问题还不完整,答案也没有达成共识,似乎存在的解决方案中也有很大一部分没有达成共识。

考虑一下专门为脚本设计的ClojureScript环境

请注意,虽然它同时支持ClojureScript(JAR)和NPM依赖项,但依赖项支持是一个在MacOS和Linux上运行的选项。它使用自托管ClojureScript,启动速度快,目标是JavaScriptCore

它有一个很好的界面,模仿Clojure中的一些东西,这些东西在ClojureScript中没有,例如
planck.io
类似于
Clojure.java.io
。它支持通过
tools.deps.alpha
/
deps.edn
加载依赖项

回显
stdin
非常简单:

(require '[planck.core :refer [*in* slurp]])
(print (slurp *in*))
和打印命令行参数:

(println *command-line-args*)

具有依赖项的独立脚本(即非项目)示例:

一个警告是,普朗克不支持使用npm依赖项。因此,如果您需要这些,请选择NodeJS的目标


第三种选择是使用Go编写的Clojure解释器。

我知道您要求使用非项目创建方法来实现这一点,但由于这一特定问题已经在我脑海中出现了相当长的一段时间,我想我会加入另一种选择

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

背景 我的需求清单与您前一段时间所做的几乎相同,并着手创建可执行jar文件。我不是说通过
java-jar myfile.jar
可执行,而是指自包含的uber jar,您可以像处理任何其他二进制文件一样直接执行

如果您读取了(作为jar文件附着的jar文件是zip文件),事实证明这是可能的。简短的版本是,您需要:

  • 用你需要的东西做一个肥罐子
  • 在文件开头的二进制jar内容中插入bash/bat/shell脚本
  • chmod+x
    uber jar文件(如果在windows上,请选中可执行文件框)
  • 重写jar文件元数据记录,以便插入的脚本文本不会使zip文件内部偏移无效
应该注意的是,zip文件规范实际上支持这一点。这就是自解压zip文件等的工作方式,并且生成的fat jar(在上述过程之后)仍然是一个有效的jar文件和一个有效的zip存档。所有相关的命令,如
java-jar
仍然有效,并且文件现在也可以直接从命令行执行

此外,按照上述模式,还可以添加对的支持,例如,大大加快cli脚本的启动时间

大约一年前,当我开始研究这个问题时,发现不存在一个用于重写jar文件元数据的最后一点的库。不仅在clojure中,而且在整个JVM上。这仍然让我感到震惊:jvm上所有语言的中心部署单元都是jar文件,而且没有一个库真正读取jar文件的内部内容。与实际的zip文件结构相同的内部结构,而不仅仅是java的ZipFile和friends

此外,我找不到clojure的库,它以干净的方式处理zip文件规范所要求的那种二进制结构

解决方案:

  • 八位字节具有我认为的Culjure可用二进制库最干净的接口,因此我为ZIP文件规范所需的特性编写了一个附加的支持。李>
  • 然后我创建了一个新的库,它读取和解释zip文件元数据,并且能够进行上面最后一点中描述的偏移量重写
  • 然后,我在现有的clojure库中创建了一个java脚本,以添加对clj zip meta实现的zip meta重写的支持,并添加对自定义序言脚本的支持,从而能够创建真正的可执行jar,而无需
    java-jar
  • 在此之后,我创建了一个leiningen模板来支持创建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模板中的样例命令行解析

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

命令行解析的源代码和cli应用程序的代码按照正常方式在src目录下运行

如果您想进行黑客攻击,可以更改序言脚本并重新运行
lein-bin
,新的序言将通过构建过程插入到可执行文件中。
~> 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!   
#!/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>