Skip to content

Commit

Permalink
This seems to work.
Browse files Browse the repository at this point in the history
  • Loading branch information
jsab committed May 5, 2014
1 parent 2ea9bc0 commit cf10dc3
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 15 deletions.
16 changes: 12 additions & 4 deletions README.md
@@ -1,12 +1,20 @@
# bashpipe

A this point, only a small experiment using a long lived shell process
to execute stuff faster than spawning a different process everytime
with clojure.java.shell/sh.
This small experiment maintains a long-lived bash process used to
execute system commands. The objective is not having to spawn a
process for every call like clojure.java.shell.

## Usage

FIXME
```
(bashpipe.core/sh "ls" "-la")
```

## Issues

Having a single bash process means that commands are executed sequentially and that if one command hangs, it will block all other commands.

A named pipe is created in the mechanism of communication with the bash process. That named pipe is never removed.

## License

Expand Down
10 changes: 7 additions & 3 deletions project.clj
@@ -1,6 +1,10 @@
(defproject bashpipe "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:description "Library maintaining a long-lived bash shell in order
not to spawn a process everytime you shell out."
:url "http://github.com/jsab/bashpipe"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]])
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.5.1"]]
:global-vars {*warn-on-reflection* true
*assert* false})
43 changes: 35 additions & 8 deletions src/bashpipe/core.clj
@@ -1,8 +1,10 @@
(ns bashpipe.core
(:require [clojure.java.io :as io]))
(:require [clojure.java.io :as io]
[clojure.java.shell :as shell]))


(defn run [^String cmd]
(defn- run [cmd]
"Used to build the long lived bash process."
(let [string-array ^"[Ljava.lang.String;" (into-array String cmd)
builder (ProcessBuilder. string-array)
process (.start builder)]
Expand All @@ -12,19 +14,44 @@
:process process}))


(def bash (run ["bash"]))
(defonce bash (run ["bash"]))
(defonce bash-lock (Object.))
(defonce pipe-path (clojure.string/trim
(:out (shell/sh "mktemp" "-u" "-t" "bashpipe"))))

(shell/sh "mkfifo" pipe-path)

(defn put! [command]

(defn- send! [command]
(let [stream ^java.io.OutputStream (:in bash)]
(io/copy (java.io.StringReader. (str command "\n"))
(io/copy (java.io.StringReader. (str command "; echo -n $? > " pipe-path "\n"))
stream)
(.flush stream)))


(defn get! []
(let [stream ^java.io.InputStream (:out bash)
avail (.available stream)
(defn- read-stream [^java.io.InputStream stream]
(let [avail (.available stream)
bytes (byte-array avail)]
(.read stream bytes)
(String. bytes)))


(defn- read-out []
(read-stream (:out bash)))


(defn- read-err []
(read-stream (:err bash)))


(defn sh [& args]
"Passes the given arguments to bash.
Returns a map containing output of the command in :out and :err
and the exit code in :exit."
(locking bash-lock
(send! (apply str args))
(let [exit-code (Long/decode (slurp pipe-path))]
{:exit exit-code
:out (read-out)
:err (read-err)})))

0 comments on commit cf10dc3

Please sign in to comment.