6

我在 Clojure 中编写了一个程序,我想在命令行上执行它,而不是在命令行专门调用 java(例如java -jar)。我想要一个可执行文件,例如myprogram,接受任何参数并运行我的程序。这里有几件事可能会使这更容易:

  1. 可以假设 Java 已安装并且java在路径中。
  2. 尽管适用于 Windows 的解决方案将是一个很好的优势,但您可以假设这一切都是在类似 UNIX 的操作系统上完成的,例如 Mac OS X 或 Ubuntu。
  3. 可以在某种脚本中调用 Java。
  4. 可以使用用户可能安装或未安装的其他语言,例如 Ruby、Python 或 Perl。All-bash 会很酷,因为我可以假设人们拥有它。
  5. 如果我必须使用某种工具来构建将执行的二进制文件,那没关系,但我不是在寻找期望使用 GUI 界面操作的 .app 或 .exe 文件(例如,Oracle 的 appbundler 不是什么我在这里找)。

我已经用一种方法在这条路上走得很远,但我不得不想知道是否有更好的方法。

仅供参考:我已经尝试过的

我将在下面描述我的方法,但任何答案都不需要遵循这种方法。

我所做的是创建一个名为 lein 的插件makescript,该插件生成一个 uberjar,base64 对其进行编码,并将其放在一个 Ruby 脚本中的一个所谓的 heredoc 变量中,如下所示:

# ...ruby script...
BASE64_JAR = <<-JAR_BOUNDARY
# [...base64 encoded file here...]
JAR_BOUNDARY

然后,您应该能够运行 ruby​​ 脚本。它将获取 BASE64_JAR 变量,将其取消编码,将其放置在临时文件中,然后通过调用java -jar <filename>.

我使用这种方法遇到的问题是 Ruby 的base64库和 Clojure 的clojure.data.codec.base64库似乎生成了不同的字符串来表示 jar,如果我使用 Ruby,由 Clojure 编码的字符串不会解码为原始文件。这可能与两种语言之间的字符串本身的编码(UTF-8 相关?也许)有关。以下是说明断开连接的 repl/irb 会话:

repl=> (def jar-contents (slurp "../target/myproj-0.1.0-SNAPSHOT-002-standalone.jar"))
repl=> (count jar-contents) ;; => 9433328
repl=> (def a-str (String. (clojure.data.codec.base64/encode (.getBytes jar-contents)) "UTF-8"))
repl=> (count a-str)        ;; => 23265576

irb> f = File.open("target/pwgen-0.1.0-SNAPSHOT-002-standalone.jar", "r").read()
irb> p f.length   # => 9657639
irb> b = Base64.encode64(f)
irb> p b.length   # => 13564973

请注意,原始大小接近但不相同,但编码版本却大不相同。

虽然这很令人费解,我想知道为什么会发生这种情况,但我想我可以通过让 makescript 生成 uberjar 并将路径传递给另一个 Ruby 脚本,然后将 base64 编码(然后解码,也使用Ruby) 独立的 JAR。问题仍然存在:有没有更好、更简单的方法?我是否遗漏了一些明显的东西,或者这真的像看起来那么难?

4

4 回答 4

4

如果您真的想这样做,您可以在 bash 可执行文件中对 jar 文件(或任何其他二进制文件)进行uuencode,例如:Embed a Executable Binary in a shell script

于 2013-11-01T20:53:25.663 回答
2

听起来您只是想使用更简洁的语法从脚本引导您的应用程序。这可能最容易做到,如下所示:

创建一个名为的新 bash 脚本myprogram

#!/usr/bin/bash
# pass whatever command line args you have down through the script
java -jar myjar.jar

给它执行权限

chmod +x myprogram

运行

./myprogram (with whatever params)

如果你想摆脱 ,./你需要移动一些东西,这样脚本就会被你的PATH.

请记住,您不是在创建特定于平台的二进制可执行文件。这样做几乎会破坏首先使用 jvm 的目的。您只是通过额外的间接层调用它。

于 2013-11-01T20:28:51.150 回答
2

现在GraalVM已经成熟了一点,您可能需要考虑将您的应用程序编译为二进制可执行文件。

有一个很好的教程来说明如何做到这一点here

于 2019-09-04T16:22:52.450 回答
1

将 clojure 作为解释性脚本运行怎么样?创建一个名为的新文件mytest

#!/usr/bin/clojure
(print "hello world")

接着

chmod +x mytest

然后简单地运行它

./mytest

这有很多缺点。启动真的很慢。这个简单的 1 班轮运行时间超过 2 秒!你只会得到一个文件。可能这仅对最简单的事情有用。但是,如果您只是想快速做一些不是生产或测试的事情,这可能会很有用。

于 2019-09-02T20:06:45.660 回答