【问题标题】:"inferior" process in clojure - redirecting asyncronous output for further processingclojure 中的“劣质”进程 - 重定向异步输出以进行进一步处理
【发布时间】:2016-08-23 14:36:04
【问题描述】:

我想在 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 中不打印有点奇怪(子问题...为什么不是 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 子流程的输出以供将来处理?

【问题讨论】:

  • 现在我已经阅读了更多关于期货的内容,我可以看到“第一次尝试”有点愚蠢。未来不会完成,这就是进程挂起的原因。不确定with-out-str 尝试有什么问题!

标签: shell clojure future


【解决方案1】:

据我所知,没有用于管理流程的 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}))

【讨论】:

  • Conch 在这个级别提供了一个包装器(sh/procin their examples)。我看到的是pythoncat 的行为不同——如果我将1+1\n 提供给cat,我会回复1+1。但是如果我把它喂给python,我什么也得不到(而我希望得到2)。
  • 我现在可以从 python 获取输出以进行打印,但需要弄清楚如何访问它以进行进一步处理。
【解决方案2】:

您可以将 sh/proc 返回的 InputStream 用 reader 打开,然后使用 line-seq 创建一个惰性输出行序列。这应该在另一个线程上运行,因为 doseq 将阻塞直到输出可用。

(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))))

【讨论】:

    【解决方案3】:

    这已经足够开始了。

    (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)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-19
      • 2021-12-19
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 1970-01-01
      • 2021-09-12
      • 1970-01-01
      相关资源
      最近更新 更多