diff options
Diffstat (limited to 'src/clojure')
-rw-r--r-- | src/clojure/contrib/shell_out.clj | 70 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib.clj | 2 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/shell_out.clj | 41 |
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 |