Skip to content
Back to skills

Clojure Babashka Process

ASecurity

Clojure library for shelling out and spawning sub-processes. Use when working with external programs, command execution, piping between processes, or handling process I/O streams.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
documentationgojavashellbashnodegitapidocumentation

Works with

  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill clojure-babashka-process --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Clojure Babashka Process?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Clojure Babashka Process
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-clojure-babashka-process/badge)](https://www.skillsdirectory.com/skills/majiayu000-clojure-babashka-process)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: clojure-babashka-process
description: Clojure library for shelling out and spawning sub-processes. Use when working with external programs, command execution, piping between processes, or handling process I/O streams.
---

# babashka.process

babashka.process is a Clojure library for shelling out and spawning sub-processes. It provides a simple, high-level API with support for piping, streaming I/O, and async execution.

## Setup

Built into babashka since v0.2.3. For JVM projects:

deps.edn:
```clojure
babashka/process {:mvn/version "0.5.22"}
```

Leiningen:
```clojure
[babashka/process "0.5.22"]
```

See https://clojars.org/babashka/process for the latest version.

## Quick Start

Most common: use `shell` for quick command execution:

```clojure
(require '[babashka.process :refer [shell]])

;; Execute and stream output (throws on non-zero exit)
(shell "ls" "-la")

;; Capture output as string
(-> (shell {:out :string} "ls" "-la") :out)
;; => "total 144\ndrwxr-xr-x ..."

;; Don't throw on error, handle exit code
(-> (shell {:continue true} "ls nothing") :exit)
;; => 1
```

For async/streaming needs, use `process`:

```clojure
(require '[babashka.process :refer [process check]])

;; Launch process, don't wait
(def proc (process {:out :string} "ls"))

;; Wait for completion and check exit
(-> proc check :out)
```

## Core Functions

### shell - High-level execution

The recommended function for most use cases:

```clojure
;; Inherits I/O, throws on error, kills subprocesses on shutdown
(shell "ls" "-la")

;; First arg is auto-tokenized
(shell "ls -la")  ; same as above

;; Capture output
(shell {:out :string} "git status")

;; Change directory
(shell {:dir "src"} "ls")

;; Add environment variables
(shell {:extra-env {"FOO" "BAR"}} "env")

;; Don't throw on error
(shell {:continue true} "false")
```

### process - Low-level control

Use when you need async processing or custom I/O handling:

```clojure
;; Launch async, returns immediately
(def p (process "long-running-command"))

;; Check if running
(alive? p)  ; => true

;; Wait for completion
@p  ; blocks until done, adds :exit

;; Or check and throw on error
(check p)
```

### sh - Like clojure.java.shell/sh

Convenience wrapper that captures output and blocks:

```clojure
(require '[babashka.process :refer [sh]])

(-> (sh "ls") :out)
;; => "file1.txt\nfile2.txt\n"

;; Similar to clojure.java.shell/sh but doesn't throw
(-> (sh "false") :exit)
;; => 1
```

## Piping Processes

Thread output from one process to another:

```clojure
;; Using shell (need {:out :string} for next process)
(let [ls-result (shell {:out :string} "ls")]
  (shell {:in (:out ls-result)} "grep" "md"))

;; Using process (stream-based, more efficient)
(-> (process "ls")
    (process {:out :string} "grep" "md")
    deref
    :out)
;; => "README.md\n"

;; Multiple pipes
(-> (process "cat" "file.txt")
    (process "grep" "error")
    (process {:out :string} "wc" "-l")
    check
    :out)
```

## I/O Options

### Output Capture

```clojure
;; As string
{:out :string}

;; As byte array
{:out :bytes}

;; Inherit (print to console)
{:out :inherit}

;; Write to file
{:out :write :out-file (io/file "output.txt")}
{:out "/tmp/output.txt"}  ; shorthand

;; Append to file
{:out :append :out-file (io/file "log.txt")}

;; Discard output
{:out :discard}

;; Redirect stderr to stdout
{:err :out}
```

### Input Sources

```clojure
;; String input
{:in "hello world"}

;; From file
{:in (io/file "input.txt")}

;; From stream
{:in (io/input-stream "data")}

;; From previous process (piping)
{:prev some-process}
```

## Streaming I/O

```clojure
(require '[clojure.java.io :as io])

;; Feed input while running
(def cat-proc (process "cat"))
(def stdin (io/writer (:in cat-proc)))
(binding [*out* stdin]
  (println "hello"))
(.close stdin)
(slurp (:out cat-proc))  ; => "hello\n"

;; Read output line by line
(def proc (process {:err :inherit} "tail" "-f" "log.txt"))
(with-open [rdr (io/reader (:out proc))]
  (binding [*in* rdr]
    (when-let [line (read-line)]
      (println "Got:" line))))
(destroy-tree proc)
```

## Process Management

```clojure
;; Check if running
(alive? proc)

;; Destroy process
(destroy proc)

;; Destroy process and all descendants (JDK9+)
(destroy-tree proc)

;; Shutdown hook
(process {:shutdown destroy-tree} "long-running")
```

## Pipelines

Use `pipeline` with `pb` for JDK9+ native pipelines:

```clojure
(require '[babashka.process :refer [pipeline pb]])

;; Create pipeline
(def pipes (pipeline (pb "ls") (pb "grep" "txt") (pb "wc" "-l")))

;; Get result from last process
(-> pipes last :out slurp)

;; Check all processes in pipeline
(run! check pipes)
```

## Common Patterns

```clojure
;; Auto-tokenization (shell only)
(shell "ls -la")  ; => ["ls" "-la"]
(shell "git commit -m" "msg")  ; => ["git" "commit" "-m" "msg"]

;; Error handling
(try
  (shell "false")
  (catch Exception e
    (println "Failed")))

;; Or handle exit yourself
(let [result (shell {:continue true} "false")]
  (when (not= 0 (:exit result))
    (println "Exit:" (:exit result))))

;; Directory and environment
(shell {:dir "src/main"} "ls")
(shell {:extra-env {"API_KEY" "secret"}} "node" "script.js")

;; Debug commands
(shell {:pre-start-fn #(println "Running:" (:cmd %))} "ls")
```

## Key Gotchas

1. **Output buffering deadlock**: Always provide `:out` option for large output:
   ```clojure
   ;; BAD - deadlocks
   (-> (process {:in large-string} "cat") check)
   ;; GOOD
   (-> (process {:out :string :in large-string} "cat") check)
   ```

2. **Deref before reading output**: Must deref process before accessing `:out :string` or `:out :bytes`.

3. **shell vs process defaults**: `shell` defaults to `:inherit` (console I/O), `process` uses buffered streams.

4. **Windows quirks**: `.ps1` scripts need `powershell.exe -File`, env vars are case-sensitive in `:extra-env`.

5. **:continue only for exit codes**: Program-not-found errors still throw even with `{:continue true}`.

## Advanced Features

For these features, see the [API reference](references/API.md):

- `$` macro - convenience macro with interpolation
- `exec` - replace current process (Unix exec call, GraalVM only)
- Custom process builders with `pb` and `start`
- Exit callbacks with `:exit-fn` (JDK11+)
- Custom program resolvers
- Integration with promesa for promises

## References

- [Full API documentation](references/API.md)
- GitHub: https://github.com/babashka/process
- README: https://github.com/babashka/process/blob/master/README.md

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.