Fixed
Details
Assignee
UnassignedUnassignedReporter
Alex MillerAlex MillerLabels
Approval
OkPatch
CodePriority
MajorAffects versions
Fix versions
Details
Details
Assignee
Unassigned
UnassignedReporter
Alex Miller
Alex MillerLabels
Approval
Ok
Patch
Code
Priority

Affects versions
Fix versions
Created March 14, 2023 at 9:17 PM
Updated April 14, 2023 at 2:03 PM
Resolved April 14, 2023 at 2:03 PM
For a long time, we’ve had the clojure.java.shell api in the core library, but it does not take advantage of the newer I/O support available in the Java process API in more recent JVMs. This proposal adds a new namespace clojure.java.process with support for wrapping the Java process API and providing some conveniences for common use cases.
The primary api is
start
:------------------------- clojure.java.process/start ([& opts+args]) Starts an external command as args and optional leading opts map: :in - a ProcessBuilder.Redirect (default = :pipe) or :inherit :out - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard :err - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard :stdout :dir - directory to run the command from, default="." :env - {env-var value} of environment variables (all strings) Returns an ILookup containing the java.lang.Process in :process and the streams :in :out :err. The map is also an IDeref that waits for process exit and returns the exit code.
A helper is provided to make ProcessBuilder.Redirect instances that read from (for :in) or write to (for :out and :err) a stream:
------------------------- clojure.java.process/from-file ([file]) Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect reading from the file. This can be passed to 'start' in :in. ------------------------- clojure.java.process/to-file ([file & {:keys [append], :as opts}]) Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect writing to the file. Set ':append' in opts to append. This can be passed to 'start' in :out or :err.
The return value of start is a map with keys :process :in :out :err that also can be deref’ed to wait for the process to exit. For the common case of waiting for a process to exit successfully, use
ok?
:------------------------- clojure.java.process/ok? ([process-map]) Given the map returned from 'start', wait for the process to exit and then return true on success
To capture a stream to a string, use
capture
:------------------------- clojure.java.process/capture ([input-stream & opts]) Read from input-stream until EOF and return a String (or nil if 0 length). Takes same opts as clojure.java.io/copy - :buffer and :encoding
A useful helper covers the common case of executing a process, waiting for exit, and capturing the stdout to a string:
------------------------- clojure.java.process/exec ([& opts+args]) Execute a command and on successful exit, return the captured output, else throw RuntimeException. Args are the same as 'start' and options if supplied override the default 'exec' settings.
Some examples:
(use 'clojure.java.process) ;; shell out and inherit the i/o (start {:out :inherit, :err :stdout} "ls" "-l") ;; write out and err to files, wait for process to exit, return exit code @(start {:out (to-file "out") :err (to-file "err")} "ls" "-l") ;; capture output to string (-> (start "ls" "-l") :out capture) ;; with exec (exec "ls" "-l") ;; read input from file (exec {:in (from-file "deps.edn")} "wc" "-l")
Patch: clj-2759-4.patch
Pulled build compilation changes for this namespace out of the patch, will include in https://clojure.atlassian.net/browse/CLJ-2761 so the patches can apply.
Screened by: fogus