When you are working in Clojure REPL and want do something in shell. How to do it ? Most of the time I will Ctrl + C break from REPL and switch back after shell interaction.

And its inconvenient, and most importantly, if I already defined something in REPL , I have to restore them after switching back.

Another work around is open two terminal windows, this resolve the second problem. But it will be better we can do it directly in Clojure REPL.

Here is a way to do that, we will use java.lang.Process of the JVM to execute external command and store the output in Clojure vector. Then we can print it or do anything we can by using Clojure's all kinds of functions.

 
(defn shell [cmd arg] 
  (let [
         p (.exec (Runtime/getRuntime) (str cmd " " arg))
         ip (.getInputStream p)
         ipr ( java.io.InputStreamReader. ip)
         br (java.io.BufferedReader. ipr)
       ]
    (.waitFor p)
 
    (loop [str (.readLine br) output []]
      (if (nil? str)
        output
        (recur (.readLine br) (conj output str))
      )
    )
  )
)
 

What we do here? It simply executes the command and arguments input to shell function in a Process, and wait it to finish and then using a loop to read each from the input stream of the process and add to a vector. And return that vector at the end.

Now we can do something with the output. For example, print to Clojure REPL.

A simple print function may looks like this.

 
 
(defn printlnv [v]
  (loop [i 0]
    (if (>= i (count v))
        ""
        (do
          (println (v i))
          (recur (inc i))
        )
    )
  )
)
 

If I want to cat content of a file.

 
 (printlnv (shell "cat"  "test.php"))
 

Or make it shorter

 
(defn cat [file] 
  (printlnv (shell "cat" file))
)
(cat "test.php")
 

If you want execute built in command like dir in windows cmd. Do like this

 
 
(shell "cmd /c dir " "")