shuck

Getting Started

Shuck is a Rust toolkit for working with shell scripts: linting, formatting, editor feedback, and managed shell execution from one CLI.

  • โšก Lint 20-100x faster than ShellCheck in benchmarked runs, with per-file caching for incremental checks.
  • ๐Ÿš Work across bash, sh/POSIX, dash, ksh, mksh, and zsh from the same binary.
  • ๐Ÿงฉ Check shell scripts and embedded GitHub Actions run: blocks with parser-backed diagnostics.
  • ๐Ÿช„ Format shell files from the command line, CI, or an editor through LSP formatting.
  • ๐Ÿ–ฅ๏ธ Use the LSP server for live diagnostics, quick fixes, hover help, navigation, and format-on-save.
  • ๐Ÿงช Run scripts with managed shell interpreters when you need reproducible local or CI behavior.
  • ๐Ÿค Keep ShellCheck-compatible suppressions and CLI migration paths for existing workflows.
  • ๐Ÿ“ฆ Install one dependency-free executable for local development, editors, and CI.

Shuck is built around one shared shell parser, so the CLI, formatter, linter, and language server agree on how each script is understood.

Installation

PyPI

pip install shuck-cli

The PyPI package name is shuck-cli, but it still installs the shuck command.

From source

cargo install shuck-cli

Pre-built binaries

Pre-built binaries are available for macOS (aarch64) and Linux (x86_64) from the releases page.

Linting

# Check files and directories
shuck check script.sh src/
 
# Check the current directory
shuck check .
 
# Check embedded GitHub Actions `run:` blocks
shuck check .github/workflows/ci.yml
 
# Read from stdin
echo 'echo $foo' | shuck check -
 
# Apply safe fixes automatically
shuck check --fix .

See the dedicated Linting guide for rule selection, fixes, output formats, watch mode, per-file shell overrides, and CI behavior.

Formatting

Shuck includes a formatter for standalone shell files:

# Format the current project in place
shuck format .
 
# Check formatting in CI
shuck format --check .
 
# Print a diff without changing files
shuck format --diff .

See the dedicated Formatting guide for formatter config, stdin usage, dialect overrides, and LSP formatting support.

Pre-commit

Use the published wheel-backed hook when you want pre-commit support without a local Rust toolchain:

repos:
  - repo: https://github.com/ewhauser/shuck
    rev: v0.0.35
    hooks:
      - id: shuck

Replace v0.0.35 with the release you want to pin.

Use the source fallback instead when you prefer to run from the Rust checkout or need an unsupported platform:

repos:
  - repo: https://github.com/ewhauser/shuck
    rev: v0.0.35
    hooks:
      - id: shuck-src

The shuck-src hook requires a working Rust toolchain because it runs cargo run -p shuck-cli -- check ... from the cloned hook repository.

LSP server

Shuck also ships with a first-party LSP server over stdio:

shuck server

See the dedicated LSP Server guide for setup examples in Neovim, Vim, Emacs, and Zed.

Managed runtime

Shuck can also run scripts with managed shell interpreters instead of whatever version happens to be installed on your machine.

# Run with the resolved interpreter for this script
shuck run deploy.sh
 
# Pin a specific shell version
shuck run --shell bash --shell-version 5.2 deploy.sh

See the dedicated Managed Runtime guide for version resolution, inline metadata, [run] config, shuck install, and shuck shell.

Suppression

Use inline comments to suppress warnings for a line, a command, or a whole file. Shuck supports both native # shuck: directives and ShellCheck-compatible # shellcheck directives, including mapped SC codes.

See the dedicated Suppression guide for examples of both syntaxes.

Zsh support

Shuck treats zsh as a first-class dialect and can resolve common oh-my-zsh plugin setups in real startup files such as .zshrc.

See the dedicated Zsh Support guide for dotfile detection, plugin-resolution examples, and the zsh-specific config and CLI flags.

ShellCheck compatibility mode

If you already have tools that invoke shellcheck, Shuck can expose a ShellCheck-shaped CLI without changing your primary workflow all at once.

See the dedicated ShellCheck compatibility guide for activation, supported flags, .shellcheckrc handling, and current caveats.

Use Shuck with rules_lint

If you use rules_lint for shell scripts in Bazel, you can keep the existing ShellCheck-shaped lint hook and swap in Shuck underneath it.

See the dedicated rules_lint replacement example for the Bazel targets and compatibility setup.

Embedded scripts

Shuck also lints shell embedded in supported GitHub Actions workflows and composite actions. See the dedicated Embedded Scripts guide for supported files, shell resolution, diagnostic remapping, and suppression behavior inside run: blocks.