Library to execute binaries on Linux

Hi folks,

For some time I’m playing with Clojure and finally I found something that I actually need to write for myself. I want to replace a couple of Bash scripts by JVM hosted Clojure. I know that Babashka or maybe other languages are more focused on projects like this but… I don’t write this for a client, just for myself to learn / have fun + create something useful.

Could you recommend please a library to execute binaries on Linux (only Linux)? Preferably Clojure but obviously Java is OK too.

These are my wishes for features ordered from the most important to the least important:

  1. execute an Linux executable like /usr/bin/ls (I don’t want to run ls - just example) with a collection of arguments like ["-l" "/home/user/Downloads"]
  2. capture stdout and stderr and exit code
  3. possibility to pipe (stdin) into the executable like: echo 'user:I changed my password' | chpasswd
  4. wait to finish or not when it can take longer to finish (find, rsync…)
  5. execute over SSH, for example ssh computer1 rm -r /home/user/abc.pdf
  6. stdin over SSH, for example cat foo.txt | ssh computer1 'cat > /tmp/bar.txt'

P.S. Thank you very much for this amazing community. I learned a lot just by googling site:clojureverse.org question ;-).

The babashka.process library can do this.

There are a few functions you can use, but most of the time you need shell, sh or process:

In your case, sh comes closest to what you want. It captures stdout and stderr to a string an captures the exit code.

(def ls-proc (sh "ls" "-l" "/home/user/Downloads"))
(:out ls-proc) ;; stdout as string
(:err ls-proc) ;; stderr as string
(:exit ls-proc) ;; the exit code

To send a string to stdin:

(sh {:in "user: I changed my password"} "chpasswd")

All function in babashka.process take an optional options map first followed by one or more strings. To do the above with the shell function:

(shell {:out :string :err :string :in "hello"} "cat")

In addition to what sh does: shell also throws an exception when the exit code is non-zero. You can do this manually using the check function: (check (sh ...)).

sh and shell are blocking - they wait for the process to finish first. When you don’t want this, use process, the most low level function in babashka.process:

(def find-proc (process "find" "."))
@find-proc ;; deref waits for the process to finish

See the babashka.process docs for more details:

Note that clojure 1.12.0-alpha2 features a very similar process library under clojure.java.process.
The shell function is called exec there and the process function is called start over there. Note that the exec function in babashka.process does something very different: it replaces the parent process with the new process.

1 Like

That’s fantastic. I thought I might need to use some Java lib and I’d not even imagine they’re two Clojure native options. Thank you very much.

Babashka.process is more convenient since it works on JVM and in future I might want to use it with Babashka ;-). Great project!

If I use the process function, is it possible to real-time process (for example filter and save) stdout / stderr coming out of the process?

@martin Yes, you can do this. It’s documented here:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.