Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Piping a subprocesses output directly to stdout (java/clojure)

Tags:

java

clojure

exec

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.

like image 735
Mediocre Gopher Avatar asked Jan 18 '13 22:01

Mediocre Gopher


2 Answers

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.

like image 120
Arthur Ulfeldt Avatar answered Oct 21 '22 03:10

Arthur Ulfeldt


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]      
        ))
like image 32
Mediocre Gopher Avatar answered Oct 21 '22 05:10

Mediocre Gopher