Managed Runtime
shuck run executes shell scripts with a resolved interpreter version instead of whatever shell happens to be on your machine. That gives you a lightweight way to pin Bash, Zsh, Dash, or mksh versions for local scripts, CI jobs, and one-off commands.
Managed interpreters are downloaded on demand, cached globally, and reused across projects. The related shuck install and shuck shell commands use the same resolver and cache.
Basic usage
# Run a script with the shell and version Shuck resolves for it
shuck run deploy.sh
# Pin both the shell and version from the CLI
shuck run --shell bash --shell-version 5.2 deploy.sh
# Run an inline command string
shuck run --shell zsh --shell-version 5.9 -c 'print $ZSH_VERSION'
# Read the script from stdin
printf 'echo hello\n' | shuck run --shell dash -
# Pass arguments through to the script
shuck run deploy.sh -- --env staging --forceSupported managed shells today are bash, zsh, dash, and mksh.
How resolution works
Shuck resolves the shell name and version separately.
For the shell name, precedence is:
--shell- Inline script metadata
[run].shellinshuck.tomlor.shuck.toml- Shebang or file-extension inference
- Default
bash
For the version constraint, precedence is:
--shell-version- Inline script metadata
[run.shells].<shell>[run].shell-versionlatest
[run.shells] is evaluated after the shell has been resolved, so you can keep different default pins for different interpreters in the same project.
shuck run has one special fallback: if you do not provide a script path, metadata, config, shell, or version, it uses the system bash on your PATH. As soon as Shuck resolves something more specific, it uses the managed-runtime path instead.
Inline script metadata
Scripts can declare their runtime requirements in a leading metadata block:
# /// shuck
# shell = "bash"
# version = ">=5.1,<6"
# [metadata]
# description = "Deploy the staging stack"
# ///
set -euo pipefailThe metadata block must stay in the leading comment header, and only one # /// shuck block is allowed per script.
Shuck currently understands:
shellversion- an optional
[metadata]table for informational fields such asdescription
Unknown keys are rejected so typos fail fast.
Project defaults
Use the [run] section in shuck.toml or .shuck.toml to set project-wide defaults:
[run]
shell = "bash"
shell-version = "5.2"
[run.shells]
bash = "5.2"
zsh = "5.9"
dash = "0.5.12"
mksh = "59c"This is especially useful when a repository mixes shell dialects or needs to hold Bash on a newer version than the one that ships with macOS.
Runtime commands follow the same config discovery and override model as shuck check, including --config and --isolated. See Configuration for the full config search rules and Settings Reference for the generated key reference.
System vs managed interpreters
Use --system when you explicitly want the interpreter already installed on your machine:
shuck run --system --shell bash --shell-version '>=5.1' deploy.shShuck still validates the system interpreter version against your requested constraint. If the version does not match, the command fails and tells you to install a managed version instead.
Without --system, Shuck downloads the matching interpreter into ~/.shuck/shells/ and reuses that cached copy for later runs.
Related commands
shuck install preloads interpreters without running a script:
shuck install bash 5.2
shuck install --list
shuck install --list bashshuck shell starts an interactive managed shell session:
shuck shell --shell bash --shell-version 5.2Unlike shuck run, shuck shell defaults to a managed Bash session when nothing more specific is configured.
Runtime environment
Before executing a script, Shuck exports a few environment variables describing the resolved interpreter:
SHUCK_SHELLSHUCK_SHELL_VERSIONSHUCK_SHELL_PATH
For managed interpreters, it also sets SHELL to the resolved binary path.
Current scope
Managed runtime support currently targets Unix-like environments with published managed shell artifacts. shuck run does not support Windows yet.