【问题标题】:How to drop into a repl from within a Clojure command line application?如何从 Clojure 命令行应用程序中放入 repl?
【发布时间】:2018-11-07 08:34:15
【问题描述】:

我正在编写一个 Clojure CLI 应用程序,我想允许给它一个命令,这会将人们放入 clojure.main REPL。

有人知道我该怎么做吗?我的努力没有结果。

编辑:

由于我的 CLI 采用管道输入这一事实,我的问题变得更糟。这是我想要工作的一个例子:

(ns playground.core
  (:gen-class))

(defn -main
  [& args]
  (with-open [r (io/reader *in*)]
    (doseq [line (line-seq r)]
      (println line)))
;; Drop in repl after having read the input and printed its content)

然后我这样称呼它:

cat ./somefile.txt | lein trampoline run

【问题讨论】:

  • 我不认为这是重复的,因为该问题没有解决 repl 不需要成为程序输入的第一个消费者并且不需要在程序开始。
  • 是的,我的问题是专门试图在 REPL 不是第一个开始的地方得到答案。程序首先启动,接受来自 in 的输入,做更多的事情,然后它需要转换为 REPL。
  • CLI REPL 在标准输入和标准输出上运行,管道也是如此。也许您应该将 nREPL / socket REPL 与 pREPL 一起使用并远程连接?这使您的标准输入为您的文件管道打开,但允许您远程连接 REPL。或者,您可以阅读 stdin then start a REPL,如其他答案中所述。
  • @Olical 我可以走那条路,但问题是,当我加入 repl 时,我已经完成了标准输入。但似乎有关 stdin 的某些东西导致 REPL 在启动后立即关闭。

标签: clojure command-line-interface read-eval-print-loop


【解决方案1】:

也许开始一个无头 REPL 并有两个单独的步骤可能对你有用?

第 1 步

启动一个无头 REPL,等待它启动

$ lein repl :headless :host 0.0.0.0 :port 9000
nREPL server started on port 9000 on host 0.0.0.0 - nrepl://0.0.0.0:9000

第 2 步

在另一个 shell 中,使用您的命令将命令发送到 REPL:

$ cat test.txt 
(def hello "Hello world!")

$ cat test.txt | lein repl :connect 0.0.0.0:9000
Connecting to nREPL at 0.0.0.0:9000
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
OpenJDK 64-Bit Server VM 1.8.0_181-8u181-b13-1ubuntu0.18.04.1-b13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

pipetest.core=> (def hello "Hello world!")
#'pipetest.core/hello
pipetest.core=> Bye for now!

第 3 步

您可以连接到 REPL,在状态从上一步更改后继续,但现在您可以使用它进行交互。

$ lein repl :connect 0.0.0.0:9000
Connecting to nREPL at 0.0.0.0:9000
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
OpenJDK 64-Bit Server VM 1.8.0_181-8u181-b13-1ubuntu0.18.04.1-b13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

pipetest.core=> (str hello "!!!")
"Hello world!!!!"

【讨论】:

    【解决方案2】:

    你可以从你的程序中调用clojure.main/repl,它会开始收听stdin并'repl'ying到它;-)此时repl将开始

    user> (clojure.main/repl)
    

    然后我在终端输入(+ 1 2 3)

    user=> 6
    user=> 
    

    所以我在 repl 中有一个 repl 去那里模拟使用你的程序的人键入 start repl 命令的情况。

    为了提供人性化的 repl 体验,rebl-readline 项目为调用 clojure.main/repl 增加了很多:

    (rebel-readline.core/with-line-reader
      (rebel-readline.clojure.line-reader/create
        (rebel-readline.clojure.service.local/create))
      (clojure.main/repl
         :prompt (fn []) ;; prompt is handled by line-reader
         :read (rebel-readline.clojure.main/create-repl-read)))
    


    由于您的程序基本上有两个阶段,
    1. 永远从标准输入读取内容或直到标准输入永久关闭。
    2. 一旦程序死机或标准输入不再可用,无论哪种情况先从标准输入读取更多内容。

    一旦发送了一些特殊的行,您可能想要(或者您已经拥有)一个脱离with-open 的东西。一旦收到此消息,就完全退出line-seqwith-open。然后启动 repl 以便它可以获取输入文件描述符。


    一旦你让程序得到repl的输入,你的管道问题就可以在封闭的shell命令中解决。 cat 有一个特殊的参数 - (只是一个破折号,两边都有空格),上面写着“停在这里并从键盘读取直到按下 Crtl-d”
    ~ » cat a-file - | cat
    hello im a line from a file
    hello
    hello
    

    在本例中,它从文件中读取一行,将其传递给 cat 命令(替换为您的程序),然后它从键盘读取单词 hello 并将其打印出来(因此您在屏幕)

    【讨论】:

    • 我通过管道将输入输入到我的 Clojure CLI。如cat somefile.txt | lein trampoline run。在这种情况下,调用 clojure.main/repl 或使用 rebel-readline 都不起作用。如果我不通过管道输入而只运行lein trampoline run,它确实可以工作。知道我在管道输入时如何进入 repl 吗?
    • @DidierA。添加了一些关于绕过管道输入的内容以及一些关于将其从读取行中分离出来的意见
    猜你喜欢
    • 2014-09-28
    • 2014-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    相关资源
    最近更新 更多