aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clojure/contrib/shell_out.clj70
-rw-r--r--src/clojure/contrib/test_contrib.clj2
-rw-r--r--src/clojure/contrib/test_contrib/shell_out.clj41
3 files changed, 102 insertions, 11 deletions
diff --git a/src/clojure/contrib/shell_out.clj b/src/clojure/contrib/shell_out.clj
index 5bb7c97b..0e67c540 100644
--- a/src/clojure/contrib/shell_out.clj
+++ b/src/clojure/contrib/shell_out.clj
@@ -6,12 +6,27 @@
; the terms of this license.
; You must not remove this notice, or any other, from this software.
+; :dir and :env options added by Stuart Halloway
+
; Conveniently launch a sub-process providing to its stdin and
; collecting its stdout
(ns clojure.contrib.shell-out
(:import (java.io InputStreamReader OutputStreamWriter)))
+(def *sh-dir* nil)
+(def *sh-env* nil)
+
+(defmacro with-sh-dir [dir & forms]
+ "Sets the directory for use with sh, see sh for details."
+ `(binding [*sh-dir* ~dir]
+ ~@forms))
+
+(defmacro with-sh-env [env & forms]
+ "Sets the environment for use with sh, see sh for details."
+ `(binding [*sh-env* ~env]
+ ~@forms))
+
(defn- stream-seq
"Takes an InputStream and returns a lazy seq of integers from the stream."
[stream]
@@ -21,25 +36,60 @@
"Takes a seq of 'sh' arguments and returns a map of option keywords
to option values."
[args]
- (loop [[arg :as args] args opts {:cmd [] :out "UTF-8"}]
+ (loop [[arg :as args] args opts {:cmd [] :out "UTF-8" :dir *sh-dir* :env *sh-env*}]
(if-not args
opts
(if (keyword? arg)
(recur (rrest args) (assoc opts arg (second args)))
(recur (rest args) (update-in opts [:cmd] conj arg))))))
+(defn- as-env-key [arg]
+ "Helper so that callers can use symbols, keywords, or strings
+ when building an environment map."
+ (cond
+ (symbol? arg) (name arg)
+ (keyword? arg) (name arg)
+ (string? arg) arg))
+
+(defn- as-file [arg]
+ "Helper so that callers can pass a String for the :dir to sh."
+ (cond
+ (string? arg) (java.io.File. arg)
+ (nil? arg) nil
+ (instance? java.io.File arg) arg))
+
+(defn- as-env-string [arg]
+ "Helper so that callers can pass a Clojure map for the :env to sh."
+ (cond
+ (nil? arg) nil
+ (map? arg) (into-array String (map (fn [[k v]] (str (as-env-key k) "=" v)) arg))
+ true arg))
+
(defn sh
- "Passes the given strings to Runtime.exec() to launch a sub-process. An
- :in option may given followed by a String specifying text to be fed
- to the sub-process's stdin. An :out option may be given followed by
- :bytes or a String. If a String is given, it will be used as a
- character encoding name (for example \"UTF-8\" or \"ISO-8859-1\") to
- convert the sub-process's stdout to a String which is returned. If
- :bytes is given, the sub-process's stdout will be stored in a byte
- array and returned."
+ "Passes the given strings to Runtime.exec() to launch a sub-process.
+
+ Options are
+
+ :in may given followed by a String specifying text to be fed to the
+ sub-process's stdin.
+ :out option may be given followed by :bytes or a String. If a String
+ is given, it will be used as a character encoding name (for
+ example \"UTF-8\" or \"ISO-8859-1\") to convert the
+ sub-process's stdout to a String which is returned.
+ If :bytes is given, the sub-process's stdout will be stored in
+ a byte array and returned.
+ :env override the process env with a map (or the underlying Java
+ String[] if you are masochist).
+ :dir override the process dir with a String or java.io.File.
+
+ You can bind :env or :dir for multiple operations using with-sh-env
+ end with-sh-dir."
[& args]
(let [opts (parse-args args)
- proc (.exec (Runtime/getRuntime) (into-array (:cmd opts)))
+ proc (.exec (Runtime/getRuntime)
+ (into-array (:cmd opts))
+ (as-env-string (:env opts))
+ (as-file (:dir opts)))
in-stream (.getInputStream proc)]
(when (:in opts)
(with-open [osw (OutputStreamWriter. (.getOutputStream proc))]
diff --git a/src/clojure/contrib/test_contrib.clj b/src/clojure/contrib/test_contrib.clj
index 65881474..7e3e86b7 100644
--- a/src/clojure/contrib/test_contrib.clj
+++ b/src/clojure/contrib/test_contrib.clj
@@ -15,7 +15,7 @@
(ns clojure.contrib.test-contrib
(:use clojure.contrib.test-is))
-(def tests [:str-utils])
+(def tests [:str-utils :shell-out])
(defn test-name
[test]
diff --git a/src/clojure/contrib/test_contrib/shell_out.clj b/src/clojure/contrib/test_contrib/shell_out.clj
new file mode 100644
index 00000000..0bd1afbe
--- /dev/null
+++ b/src/clojure/contrib/test_contrib/shell_out.clj
@@ -0,0 +1,41 @@
+(ns clojure.contrib.test-contrib.shell-out
+ (:use clojure.contrib.test-is
+ clojure.contrib.shell-out)
+ (:import (java.io File)))
+
+; workaroung to access private parse-args. Better way?
+(def parse-args ((ns-interns 'clojure.contrib.shell-out) 'parse-args))
+(def as-file ((ns-interns 'clojure.contrib.shell-out) 'as-file))
+(def as-env-string ((ns-interns 'clojure.contrib.shell-out) 'as-env-string))
+
+(deftest test-parse-args
+ (are (= _1 _2)
+ {:cmd [nil] :out "UTF-8" :dir nil :env nil} (parse-args [])
+ {:cmd ["ls"] :out "UTF-8" :dir nil :env nil} (parse-args ["ls"])
+ {:cmd ["ls" "-l"] :out "UTF-8" :dir nil :env nil} (parse-args ["ls" "-l"])
+ {:cmd ["ls"] :out "ISO-8859-1" :dir nil :env nil} (parse-args ["ls" :out "ISO-8859-1"])
+))
+
+(deftest test-with-sh-dir
+ (are (= _1 _2)
+ nil *sh-dir*
+ "foo" (with-sh-dir "foo" *sh-dir*)))
+
+(deftest test-with-sh-env
+ (are (= _1 _2)
+ nil *sh-env*
+ {:KEY "VAL"} (with-sh-env {:KEY "VAL"} *sh-env*)))
+
+(deftest test-as-env-string
+ (are (= _1 _2)
+ nil (as-env-string nil)
+ ["FOO=BAR"] (seq (as-env-string {"FOO" "BAR"}))
+ ["FOO_SYMBOL=BAR"] (seq (as-env-string {'FOO_SYMBOL "BAR"}))
+ ["FOO_KEYWORD=BAR"] (seq (as-env-string {:FOO_KEYWORD "BAR"}))))
+
+
+(deftest test-as-file
+ (are (= _1 _2)
+ (File. "foo") (as-file "foo")
+ nil (as-file nil)
+ (File. "bar") (as-file (File. "bar")))) \ No newline at end of file