I'm looking for a way to start up a subprocess in clojure (java is fine to) and have the output of it get sent directly to stdout in real-time. The closest I've been able to find is the Conch library for clojure, which allows you to send the output to *out*
, but it doesn't actually display the output until the process is done running.
Not sure if there is a convenient Clojure wrapper for this:
(->> (.. Runtime getRuntime (exec "ls") getInputStream)
java.io.InputStreamReader.
java.io.BufferedReader.
line-seq
(map println))
It's worth noting in practice that you need to read both stdin and stderr regularly or the process can hang when one of the buffers fill.
I marked Arthur's answer as correct since it led me to the solution, and it basically is what someone would want in the basic case. I ended up building out a larger method which does more though, and figured I'd put it here in case anyone else found it useful
(defn- print-return-stream
[stream]
(let [stream-seq (->> stream
(java.io.InputStreamReader.)
(java.io.BufferedReader.)
line-seq)]
(doall (reduce
(fn [acc line]
(println line)
(if (empty? acc) line (str acc "\n" line)))
""
stream-seq))))
(defn exec-stream
"Executes a command in the given dir, streaming stdout and stderr to stdout,
and once the exec is finished returns a vector of the return code, a string of
all the stdout output, and a string of all the stderr output"
[dir command & args]
(let [runtime (Runtime/getRuntime)
proc (.exec runtime (into-array (cons command args)) nil (File. dir))
stdout (.getInputStream proc)
stderr (.getErrorStream proc)
outfut (future (print-return-stream stdout))
errfut (future (print-return-stream stderr))
proc-ret (.waitFor proc)]
[proc-ret @outfut @errfut]
))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With