Skip to content
Latchkey

just: Shebang Recipes for Python and Bash

A just recipe whose first line is a shebang runs the entire body as one script in that interpreter.

Normally each recipe line runs in its own shell. A shebang recipe runs the whole body together, so you can write multi-line Python or a real Bash script with shared state.

What it does

When a recipe body starts with a "#!" shebang, just writes the body to a temporary file and executes it with that interpreter instead of running each line separately. This means variables, loops, and exit handling persist across lines, unlike the default line-by-line execution.

Common usage

justfile
# justfile
# a Python recipe
report:
    #!/usr/bin/env python3
    import json, sys
    data = {"ok": True}
    print(json.dumps(data))

# a bash recipe with shared state and strict mode
provision:
    #!/usr/bin/env bash
    set -euo pipefail
    for svc in web worker; do
      echo "starting $svc"
    done

Syntax

FormWhat it does
#!/usr/bin/env bashRun the body as one bash script
#!/usr/bin/env python3Run the body as one python script
set -euo pipefailCommon strict-mode header inside a bash shebang recipe
(default, no shebang)Each line runs in its own shell process

In CI

Use a shebang recipe whenever a step needs loops, conditionals, or a non-shell language; it keeps the logic in the repo and out of inline YAML. Add set -euo pipefail in bash shebang recipes so a mid-script failure aborts the job rather than silently continuing.

Common errors in CI

"error: Recipe report with shebang #!/usr/bin/env python3 execution error: No such file or directory" means the interpreter is not installed on the runner. On minimal images /usr/bin/env or bash may be missing, producing "No such file or directory". Without set -e a failing command mid-recipe does not fail the recipe, so the job can pass when it should not.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →