Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
26 changes: 21 additions & 5 deletions src/babashka/process.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -265,13 +270,21 @@
: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))))
nil)
:discard (.redirectOutput pb (if-before-jdk8
(ProcessBuilder$Redirect/to @null-file)
ProcessBuilder$Redirect/DISCARD))
(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))))
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)
Expand Down Expand Up @@ -385,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
Expand Down Expand Up @@ -470,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.
Expand Down
14 changes: 14 additions & 0 deletions test/babashka/process_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,17 @@
:out)]
(is (bytes? result))
(is (= (seq ba) (seq result))))))

(defmacro if-pre-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 (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))")))))
(is (= (with-out-str (println :dude)) (slurp (:out @(p/process {:err out} bb "-e" "(println :dude) (binding [*out* *err*] (println :bye))"))))))))