From f02e024c93ec49e30a03aada3d5c94817ea4affe Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 7 Dec 2025 15:23:41 +0100 Subject: [PATCH 1/4] discard --- src/babashka/process.cljc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/babashka/process.cljc b/src/babashka/process.cljc index 24ead72..b37b66d 100644 --- a/src/babashka/process.cljc +++ b/src/babashka/process.cljc @@ -219,7 +219,12 @@ :escape default-escape :program-resolver default-program-resolver}) -(defn- normalize-opts [{:keys [:out :err :in :inherit] :as opts}] +(def ^java.io.File null-file + (delay (io/file (if windows? + "NUL" + "/dev/null")))) + +(defn- normalize-opts [{:keys [out err in inherit] :as opts}] (cond-> opts (and inherit (not out)) (-> (assoc :out :inherit)) @@ -265,12 +270,18 @@ :inherit (.redirectOutput pb ProcessBuilder$Redirect/INHERIT) :write (.redirectOutput pb (ProcessBuilder$Redirect/to (io/file (str out-file)))) :append (.redirectOutput pb (ProcessBuilder$Redirect/appendTo (io/file (str out-file)))) + :discard (.redirectOutput pb (if-before-jdk8 + (ProcessBuilder$Redirect/to @null-file) + ProcessBuilder$Redirect/DISCARD)) nil) (case err :out (.redirectErrorStream pb true) :inherit (.redirectError pb ProcessBuilder$Redirect/INHERIT) :write (.redirectError pb (ProcessBuilder$Redirect/to (io/file (str err-file)))) :append (.redirectError pb (ProcessBuilder$Redirect/appendTo (io/file (str err-file)))) + :discard (.redirectOutput pb (if-before-jdk8 + (ProcessBuilder$Redirect/to @null-file) + ProcessBuilder$Redirect/DISCARD)) nil) (case in :inherit (.redirectInput pb ProcessBuilder$Redirect/INHERIT) From 708671b77eee12d26e7b08079de5cf453e784a09 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 7 Dec 2025 15:59:22 +0100 Subject: [PATCH 2/4] Fix #181: support :discard or ProcessBuilder$Redirect for :out and :err --- CHANGELOG.md | 4 ++++ src/babashka/process.cljc | 21 +++++++++++++-------- test/babashka/process_test.cljc | 8 ++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1b98fd..bdbd0d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ [Babashka process](https://github.com/babashka/process) Clojure library for shelling out / spawning sub-processes +## 0.6.24 (2025-12-07) + +- [#181](https://github.com/babashka/process/issues/181): support `:discard` or `ProcessBuilder$Redirect` as `:out` and `:err` options + ## 0.6.23 (2025-03-31) - [#163](https://github.com/babashka/process/issues/163), [#164](https://github.com/babashka/process/issues/164): Program resolution strategy for `exec` and Windows now matches macOS/Linux/PowerShell ([@lread](https://github.com/lread)) diff --git a/src/babashka/process.cljc b/src/babashka/process.cljc index b37b66d..1e9bab0 100644 --- a/src/babashka/process.cljc +++ b/src/babashka/process.cljc @@ -271,18 +271,20 @@ :write (.redirectOutput pb (ProcessBuilder$Redirect/to (io/file (str out-file)))) :append (.redirectOutput pb (ProcessBuilder$Redirect/appendTo (io/file (str out-file)))) :discard (.redirectOutput pb (if-before-jdk8 - (ProcessBuilder$Redirect/to @null-file) + (ProcessBuilder$Redirect/to @null-file) ProcessBuilder$Redirect/DISCARD)) - nil) + (when (instance? java.lang.ProcessBuilder$Redirect out) + (.redirectOutput pb out))) (case err :out (.redirectErrorStream pb true) :inherit (.redirectError pb ProcessBuilder$Redirect/INHERIT) :write (.redirectError pb (ProcessBuilder$Redirect/to (io/file (str err-file)))) :append (.redirectError pb (ProcessBuilder$Redirect/appendTo (io/file (str err-file)))) - :discard (.redirectOutput pb (if-before-jdk8 - (ProcessBuilder$Redirect/to @null-file) - ProcessBuilder$Redirect/DISCARD)) - nil) + :discard (.redirectError pb (if-before-jdk8 + (ProcessBuilder$Redirect/to @null-file) + ProcessBuilder$Redirect/DISCARD)) + (when (instance? java.lang.ProcessBuilder$Redirect err) + (.redirectError pb err))) (case in :inherit (.redirectInput pb ProcessBuilder$Redirect/INHERIT) (when (or (instance? java.io.File in) @@ -396,12 +398,14 @@ stderr (.getErrorStream proc) out (if (and out (or (identical? :string out) (identical? :bytes out) - (not (keyword? out)))) + (and (not (keyword? out)) + (not (instance? java.lang.ProcessBuilder$Redirect out))))) (future (copy stdout out out-enc)) stdout) err (if (and err (or (identical? :string err) (identical? :bytes err) - (not (keyword? err)))) + (and (not (keyword? err)) + (not (instance? java.lang.ProcessBuilder$Redirect err))))) (future (copy stderr err err-enc)) stderr)] ;; wrap in futures, see https://github.com/clojure/clojure/commit/7def88afe28221ad78f8d045ddbd87b5230cb03e @@ -481,6 +485,7 @@ For writing output to a file, you can set `:out` and `:err` to a `java.io.File` object, or a keyword: - `:write` + an additional `:out-file`/`:err-file` + file to write to the file. - `:append` + an additional `:out-file`/`:err-file` + file to append to the file. + To discard `:out` or `:err`, use `:discard` - `:prev`: output from `:prev` will be piped to the input of this process. Overrides `:in`. - `:inherit`: if true, sets `:in`, `:out` and `:err` to `:inherit`. - `:dir`: working directory. diff --git a/test/babashka/process_test.cljc b/test/babashka/process_test.cljc index 08e98cf..c16ecf6 100644 --- a/test/babashka/process_test.cljc +++ b/test/babashka/process_test.cljc @@ -627,3 +627,11 @@ :out)] (is (bytes? result)) (is (= (seq ba) (seq result)))))) + +(deftest discard-test + (when-let [bb (u/find-bb)] + (doseq [out [:discard java.lang.ProcessBuilder$Redirect/DISCARD]] + (is (= "" (slurp (:out @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) + (is (= (with-out-str (println :bye)) (slurp (:err @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) + (is (= "" (slurp (:err @(p/process {:err out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) + (is (= (with-out-str (println :dude)) (slurp (:out @(p/process {:err out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))")))))))) From 97df2c0ee8c8d48c0ffb1e4445281962cd26ed2a Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 7 Dec 2025 16:06:35 +0100 Subject: [PATCH 3/4] defmacro --- test/babashka/process_test.cljc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/babashka/process_test.cljc b/test/babashka/process_test.cljc index c16ecf6..a990db8 100644 --- a/test/babashka/process_test.cljc +++ b/test/babashka/process_test.cljc @@ -628,9 +628,15 @@ (is (bytes? result)) (is (= (seq ba) (seq result)))))) +(defmacro if-jdk9+ [then else] + (if (identical? ::pre-jdk9 + (try (import 'java.lang.ProcessHandle) + (catch Exception _ ::pre-jdk9))) + then else)) + (deftest discard-test (when-let [bb (u/find-bb)] - (doseq [out [:discard java.lang.ProcessBuilder$Redirect/DISCARD]] + (doseq [out [:discard (if-jdk9+ java.lang.ProcessBuilder$Redirect/DISCARD :discard)]] (is (= "" (slurp (:out @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) (is (= (with-out-str (println :bye)) (slurp (:err @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) (is (= "" (slurp (:err @(p/process {:err out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) From 1b51891f239ea27e07afbbd148ee2de892ea51ea Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 7 Dec 2025 16:08:20 +0100 Subject: [PATCH 4/4] wip --- test/babashka/process_test.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/babashka/process_test.cljc b/test/babashka/process_test.cljc index a990db8..a36016e 100644 --- a/test/babashka/process_test.cljc +++ b/test/babashka/process_test.cljc @@ -628,7 +628,7 @@ (is (bytes? result)) (is (= (seq ba) (seq result)))))) -(defmacro if-jdk9+ [then else] +(defmacro if-pre-jdk9+ [then else] (if (identical? ::pre-jdk9 (try (import 'java.lang.ProcessHandle) (catch Exception _ ::pre-jdk9))) @@ -636,7 +636,7 @@ (deftest discard-test (when-let [bb (u/find-bb)] - (doseq [out [:discard (if-jdk9+ java.lang.ProcessBuilder$Redirect/DISCARD :discard)]] + (doseq [out [:discard (if-pre-jdk9+ :discard java.lang.ProcessBuilder$Redirect/DISCARD)]] (is (= "" (slurp (:out @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) (is (= (with-out-str (println :bye)) (slurp (:err @(p/process {:out out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))) (is (= "" (slurp (:err @(p/process {:err out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))")))))