Clojure 在脚本中读取命令行参数时出错

Clojure 在脚本中读取命令行参数时出错,clojure,Clojure,我正试图写一个库来做一些特定领域的事情。我想向这个库添加一些可以直接从命令行运行的脚本 目录布局: +- project.clj +- src/ | +- my-lib.clj | +- my-lib/ | +- my-sub-1.clj +- scripts/ +- run-command.sh src/my-lib.clj文件加载my-lib/my-sub-1.clj文件: (ns my-lib (:use [my-lib.my-sub-1]) ) sr

我正试图写一个库来做一些特定领域的事情。我想向这个库添加一些可以直接从命令行运行的脚本

目录布局:

+- project.clj
+- src/
|   +- my-lib.clj
|   +- my-lib/
|        +- my-sub-1.clj
+- scripts/
    +- run-command.sh
src/my-lib.clj文件加载my-lib/my-sub-1.clj文件:

(ns my-lib
  (:use [my-lib.my-sub-1])
)
src/my-lib/my-sub-1.clj文件包含我想要提供的函数。其中一个函数称为“convert”,并接受两个参数:输入文件名和输出文件名;它转换文件格式。要使用它:

(convert "input-file.txt" "output-file.txt")
src/my lib/my-sub-1.clj中的(做一些有趣的事情)和(转换)函数如下所示:

(defn do-something-interesting
  [input-file output-file]
  (magic-happens-here input-file output-file))

(defn convert
  [args]
  (let [infile (first args)
        outfile (second args)]
    (do-something-interesting infile outfile)))
我目前的目标是:在“scripts”目录中创建一个脚本“run command.sh”,它包含两个参数:输入文件名和输出文件名。应该可以通过以下方式运行脚本:

./run-command.sh input-file.txt output-file.txt
我确实有run-command.sh工作,只要我使用(do something)函数而不是(convert)硬编码脚本中的文件名。我还没能从论点列表中读到

运行的run-command.sh脚本:

#!/bin/sh 
java -cp "../lib/*":"../src":$CLASSPATH clojure.main -e "
(use '[my-lib my-sub-1])
(do-something-interesting "path-to-input-file" "path-to-output-file")
"
。。。但什么不起作用:

#!/bin/sh 
java -cp "../lib/*":"../src":$CLASSPATH clojure.main -e "
(use '[my-lib my-sub-1])
(convert *command-line-args*)
"
我得到的错误是:

Exception in thread "main" java.lang.IllegalArgumentException: No implementation of method: :reader of protocol: #'clojure.contrib.io/Streams found for class: nil (NO_SOURCE_FILE:0)
at clojure.lang.Compiler.eval(Compiler.java:5435)
at clojure.lang.Compiler.eval(Compiler.java:5386)
at clojure.core$eval.invoke(core.clj:2382)
at clojure.main$eval_opt.invoke(main.clj:235)
at clojure.main$initialize.invoke(main.clj:254)
at clojure.main$null_opt.invoke(main.clj:279)
at clojure.main$main.doInvoke(main.clj:354)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.lang.Var.invoke(Var.java:369)
at clojure.lang.AFn.applyToHelper(AFn.java:165)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: No implementation of method: :reader of protocol: #'clojure.contrib.io/Streams found for class: nil
at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:471)
at clojure.contrib.io$eval32$fn__33$G__23__38.invoke(io.clj:118)
at bioclojure.vcf$header.invoke(vcf.clj:22)
at bioclojure.vcf$column_header.invoke(vcf.clj:55)
at bioclojure.vcf$column_names.invoke(vcf.clj:61)
at bioclojure.vcf$vcf2tsv.invoke(vcf.clj:169)
at bioclojure.vcf$convert.invoke(vcf.clj:185)
at user$eval474.invoke(NO_SOURCE_FILE:3)
at clojure.lang.Compiler.eval(Compiler.java:5419)
... 11 more
我已经尝试在脚本文件run-command.sh本身和顶级库文件my-lib.clj中“使用”ing clojure.contrib.io,但到目前为止没有成功

如果有人能帮我,那就太好了


这是因为您没有指定cli参数。您必须调用
java。。。。clojure.main some-script.clj a b c
。然后a、b和c将包含在
*命令行args*

由于kotarak的建议,我找到了解决方案。在run-command.sh中,我需要使用bash方式而不是clojure方式引用参数。我甚至不需要那个单独的(转换)函数

脚本是什么样子的:

#!/bin/sh 
java -cp "../lib/*":"../src":$CLASSPATH clojure.main -e "
(use '[my-lib my-sub-1])
(convert *command-line-args*)
"
它应该是什么样子的:

#!/bin/sh 
java -cp "../lib/*":"../src":$CLASSPATH clojure.main -e "
(use '[my-lib my-sub-1])
(do-something-interesting \"$1\" \"$2\")
"

你应该考虑使用“Gen类”特性来处理命令。 已编译Clojure/Java函数中的行参数,而不是 通过Clojures main/repl函数动态评估Clojure代码:

(ns commandline
  (:gen-class))

(defn -main [& args]
  (convert args))
#!/bin/bash

java -cp "../lib/*":../YOURPROJECT.jar:$CLASSPATH commandline "$@"
使用leinjar创建应用程序的jar文件并传递 shell中的命令行参数与主函数的关系:

(ns commandline
  (:gen-class))

(defn -main [& args]
  (convert args))
#!/bin/bash

java -cp "../lib/*":../YOURPROJECT.jar:$CLASSPATH commandline "$@"

这仍然不是正确的做法。考虑一下,如果<代码> $1 <代码>是一个由一个双引号字符组成的字符串,将会发生什么。kotarak的回答意味着您的脚本应该将
.clj
脚本的名称及其所有命令行参数作为常规CLI参数传递到
java
命令的
clojure.main
参数之后。然后,这些参数将被传递到
clojure.main
类的
main
方法,该方法将依次运行命名脚本,同时安排在
*命令行args*
Var.Hi-Jurgen中收集后续CLI参数,我以前试过远离gen类,因为它为不了解java的人增加了额外的复杂性。但这看起来很简单:-)将尝试一下。如果您已经使用了Leiningen,则编译/jar只是另一项任务。这项工作做得非常出色。my-lib.clj文件现在看起来如下:(ns my lib(:use[my lib.my-sub-1])(:gen class))(defn-main[command&args](case命令“help”(println“Some help message”)“convert”(做一些有用的事情(第一个args)(第二个args)))然后是“lein compile”和“lein uberjar”。这允许我基于同一个jar文件提供多个命令。我知道我应该重写一点-main来处理nils,或者只使用[&args],但是谢谢!