我想在Clojure中维护一个长期运行的交互式shell进程 - 这将被称为"劣等" Emacs中的过程。我认为基本的想法非常熟悉,因为Clojure本身可以通过CIDER在Emacs内部运行。我很高兴知道使用Clojure作为顶级流程是否有任何有关此设置的实例。
编辑:我发现了这个" shell" Gist,这是一个非常好看的Conch封面,给了我一些思考的东西。
使用我自己的first attempt with Conch:我无法将字符串输入python
,并以与我相同的方式获得输出,例如cat
。但通过一些实验,我找出了基础知识并绕过了第一个障碍。
(require '[me.raynes.conch.low-level :as sh])
(def sh-python (sh/proc "python" "-i")) ; "-i" needed to get interactive mode
#'flowrweb.core/sh-python
(future (sh/stream-to-out sh-python :out))
#future[{:status :pending, :val nil} 0x516f3fff]
(sh/feed-from-string sh-python "1+1\n") ; just returns nil on the CIDER repl
2 ; <- but we see "2" with the lein repl
nil
所以我知道我感兴趣的数据是可用的,虽然它有点奇怪,它没有在CIDER中打印(子问题 ...为什么不是&n #2; 2打印?)。无论如何,对于我的用例,我不需要它打印;相反,我只是想把它作为一个字符串。
(def pyout (future (sh/stream-to-string sh-python :out)))
;=> #'flowrweb.core/py-out
(sh/feed-from-string sh-python "1+2\n")
;=> 3
;=> nil
@pyout
^C ; <- the process hangs
似乎sh/stream-to-string
并没有做我需要的事情。
with-out-str
?(def something (with-out-str (sh/feed-from-string sh-python "1+3\n")))
;=> 4
;=> #'user/something
something
;=> "" ;<- where is the "4"?
不,那也不起作用。
tl; dr :如何重定向Conch子流程的输出以供将来处理?
答案 0 :(得分:0)
据我所知,没有用于管理流程的Clojure特定机器。此外,操作系统进程不属于严格意义上的Clojure焦点,因为它是托管语言,而进程管理肯定是主机应该管理的东西(例如JVM或CLR)。
所以,让我们说你在JVM上运行Clojure。 JVM公开用于创建子进程的一个工具是ProcessBuilder,您可以从Clojure代码中调用,如下所示(credits go to @codification):
(ns proc
(:import [java.lang ProcessBuilder])
(:use [clojure.java.io :only [reader writer]]))
(defn spawn [& args]
(let [process (-> (ProcessBuilder. args)
(.start))]
{:out (-> process
(.getInputStream)
(reader))
:err (-> process
(.getErrorStream)
(reader))
:in (-> process
(.getOutputStream)
(writer))
:process process}))
答案 1 :(得分:0)
您可以使用sh / proc返回的InputStream,使用reader打开它,然后使用line-seq创建一个延迟的输出行序列。这应该在另一个线程上运行,因为剂量将阻塞,直到输出可用。
(let [{out-stream :out} (sh/proc "ls" "-l")]
(with-open [out-rdr (clojure.java.io/reader out-stream)]
(doseq [line (line-seq out-rdr)]
; do something with line: Like feed it into core.async chan
; (>!! some-chan line)
; or pass it to some fn
(println line))))
答案 2 :(得分:0)
这足以开始使用。
(require '[me.raynes.conch.low-level :as sh])
;=> nil
(def my-stringwriter (java.io.StringWriter.))
;=> #'user/my-stringwriter
(def sh-python (sh/proc "python" "-i"))
;=> #'user/sh-python
(future (sh/stream-to sh-python :out my-stringwriter)) ; NOT redirecting *out*
;=> #future[{:status :pending, :val nil} 0x4358e46d]
(sh/feed-from-string sh-python "1+1\n")
;=> nil
(.toString my-stringwriter)
;=> "2\n"
(sh/feed-from-string sh-python "1+2\n")
;=> nil
(.toString my-stringwriter)
;=> "2\n3\n"
请记住“代理系统支持以异步和独立的方式在线程之间共享更改状态”(clojure.org docs),我认为封装它的一种合理方法是:
(require '[clojure.string :as str])
(def a-stringwriter (agent (java.io.StringWriter.)))
(future (sh/stream-to sh-python :out @a-stringwriter))
(defn feed-python [user-input]
(future (sh/feed-from-string sh-python (str user-input "\n"))
(Thread/sleep 1000)
(str/split (.toString @a-stringwriter) #"\n")))
然后你可以写例如@(feed-python "10+20")
发送和接收python的结果。此命令将显示先前交互的历史记录以及最新的交互历史记录。对于大多数用例,只有最新的添加是相关的。
(defn gljcon [user-input]
(last @(feed-python user-input)))