shuck

ShellCheck Compatibility

Shuck can expose a ShellCheck-shaped CLI for teams that want to migrate existing editor integrations, CI jobs, or local workflows without changing every call site at once.

This mode is powered by Shuck's parser and linter, not by embedding ShellCheck itself. That means the interface is intentionally familiar, while the implementation continues to evolve with the rest of Shuck.

Activate compatibility mode

There are two supported ways to turn the compatibility surface on:

  1. Invoke the binary as shellcheck.
  2. Set SHUCK_SHELLCHECK_COMPAT=1 before running shuck.
# Make shuck available as `shellcheck`
ln -s "$(command -v shuck)" ~/bin/shellcheck
shellcheck --version
 
# Or force compat mode for a single command
SHUCK_SHELLCHECK_COMPAT=1 shuck --version

Compat mode only affects that invocation. Running shuck check ... directly still uses Shuck's native CLI.

What it supports today

The compatibility surface currently covers the pieces that are most useful for migration:

  • Familiar --help and --version output
  • Common output formats: tty, json, json1, checkstyle, gcc, diff, and quiet
  • ShellCheck-style code filtering with --include and --exclude
  • Severity filtering with --severity
  • Source resolution controls like --check-sourced, --external-sources, and --source-path
  • Shell overrides for sh, bash, dash, ksh, and busybox
  • Optional-check discovery with --list-optional
  • Standard stdin usage with - as the input path
# Machine-readable output for editor tooling or CI
SHUCK_SHELLCHECK_COMPAT=1 shuck -f json1 script.sh
 
# Only keep selected SC codes
SHUCK_SHELLCHECK_COMPAT=1 shuck --include=SC2086,SC2154 script.sh
 
# Follow sourced files and add extra lookup roots
SHUCK_SHELLCHECK_COMPAT=1 shuck -a -P scripts:lib main.sh

.shellcheckrc support

Compat mode searches for .shellcheckrc from the current working directory upward. You can keep the default search, skip it with --norc, or point at a specific file with --rcfile.

Supported config keys today are check-sourced, color, disable, enable, extended-analysis, external-sources, format, include, severity, shell, source-path, and wiki-link-count.

# Use the nearest .shellcheckrc
shellcheck script.sh
 
# Ignore discovered config
shellcheck --norc script.sh
 
# Use an explicit config file
shellcheck --rcfile ci/shellcheckrc script.sh

Use Shuck with rules_lint

If your Bazel repo already uses rules_lint for shell scripts, you can swap Shuck in without rewriting the lint aspect itself. The trick is to keep exposing a shellcheck executable target and let Shuck handle the CLI compatibility layer behind that name.

One workable pattern is:

  1. Fetch the platform-specific Shuck release artifacts.
  2. Re-export each downloaded binary as an executable named shellcheck.
  3. Keep lint_shellcheck_aspect pointed at your //tools:shellcheck target.
# repositories.bzl
def shuck_deps():
    SHUCK_VERSION = "<shuck-version>"
 
    http_archive(
        name = "shuck",
        build_file = "//tools:shuck.BUILD.bazel",
        sha256 = "<linux-x86_64-sha256>",
        strip_prefix = "shuck-cli-x86_64-unknown-linux-gnu",
        urls = ["https://github.com/ewhauser/shuck/releases/download/v{}/shuck-cli-x86_64-unknown-linux-gnu.tar.xz".format(SHUCK_VERSION)],
    )
 
    http_archive(
        name = "shuck_linux_arm64",
        build_file = "//tools:shuck.BUILD.bazel",
        sha256 = "<linux-arm64-sha256>",
        strip_prefix = "shuck-cli-aarch64-unknown-linux-gnu",
        urls = ["https://github.com/ewhauser/shuck/releases/download/v{}/shuck-cli-aarch64-unknown-linux-gnu.tar.xz".format(SHUCK_VERSION)],
    )
 
    http_archive(
        name = "shuck_osx",
        build_file = "//tools:shuck.BUILD.bazel",
        sha256 = "<darwin-arm64-sha256>",
        strip_prefix = "shuck-cli-aarch64-apple-darwin",
        urls = ["https://github.com/ewhauser/shuck/releases/download/v{}/shuck-cli-aarch64-apple-darwin.tar.xz".format(SHUCK_VERSION)],
    )
# tools/shuck.BUILD.bazel
package(default_visibility = ["//visibility:public"])
 
genrule(
    name = "shellcheck_bin",
    srcs = ["shuck"],
    outs = ["shellcheck"],
    cmd = "cp $(location shuck) $@",
    executable = True,
)
 
filegroup(
    name = "file",
    srcs = [":shellcheck_bin"],
)
# tools/BUILD.bazel
alias(
    name = "shellcheck",
    actual = select({
        "@bazel_tools//src/conditions:linux_x86_64": "@shuck//:shellcheck_bin",
        "@bazel_tools//src/conditions:linux_aarch64": "@shuck_linux_arm64//:shellcheck_bin",
        "@bazel_tools//src/conditions:darwin_arm64": "@shuck_osx//:shellcheck_bin",
    }),
    visibility = ["//visibility:public"],
)

For rules_lint, that alias should resolve to the executable target rather than a forwarding filegroup.

# tools/lint.bzl
load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test")
load("@aspect_rules_lint//lint:shellcheck.bzl", "lint_shellcheck_aspect")
 
shellcheck = lint_shellcheck_aspect(
    binary = "//tools:shellcheck",
    config = "//:.shellcheckrc",
)
 
shellcheck_test = lint_test(aspect = shellcheck)

That keeps your existing rules_lint entry point, target names, and .shellcheckrc file in place while swapping in Shuck as the implementation. In practice, the migration boundary stays at the tool label instead of spreading through every lint target in the repo.

Migration notes

  • Exit status follows the usual ShellCheck shape: 0 for no diagnostics, 1 when diagnostics are reported, and 2 for file or runtime errors.
  • Diagnostic codes, suppressions, and filters use SCxxxx identifiers, so existing # shellcheck disable=... comments continue to work.
  • --list-optional is available, but not every named optional check is implemented yet. Treat that catalog as a compatibility target rather than a guarantee that every check can already be enabled.
  • If you want Shuck-only features such as the native multi-dialect workflow or automatic fixes, use the regular shuck check docs instead of the compatibility surface.

Repository conformance

This table tracks the curated large-corpus repositories that Shuck uses for ShellCheck compatibility work. Green means the checked-in corpus metadata has no known reviewed repo issues. Yellow means the repo currently has known reviewed divergences or is skipped because ShellCheck does not support it in the corpus harness.

Passes checked-in corpus conformanceKnown reviewed divergences or ShellCheck unsupported
RepositoryStatus
233boy/v2ray1 known issue1 known issue
89luca89/distroboxPasses checked-in corpus conformancePasses checked-in corpus conformance
acmesh-official/acme.sh3 known issues3 known issues
alexanderepstein/Bash-Snippets2 known issues2 known issues
alpinelinux/aportsPasses checked-in corpus conformancePasses checked-in corpus conformance
angristan/openvpn-installPasses checked-in corpus conformancePasses checked-in corpus conformance
aristocratos/bashtopPasses checked-in corpus conformancePasses checked-in corpus conformance
asdf-vm/asdfPasses checked-in corpus conformancePasses checked-in corpus conformance
awslabs/git-secrets1 known issue1 known issue
basecamp/omarchyPasses checked-in corpus conformancePasses checked-in corpus conformance
Bash-it/bash-itPasses checked-in corpus conformancePasses checked-in corpus conformance
bats-core/bats-core2 known issues2 known issues
bin456789/reinstallPasses checked-in corpus conformancePasses checked-in corpus conformance
bitnami/containersPasses checked-in corpus conformancePasses checked-in corpus conformance
bittorf/kalua10 known issues10 known issues
CISOfy/lynisPasses checked-in corpus conformancePasses checked-in corpus conformance
client9/shlibPasses checked-in corpus conformancePasses checked-in corpus conformance
community-scripts/ProxmoxVE25 known issues25 known issues
dehydrated-io/dehydrated1 known issue1 known issue
docker-library/official-imagesPasses checked-in corpus conformancePasses checked-in corpus conformance
docker-mailserver/docker-mailserverPasses checked-in corpus conformancePasses checked-in corpus conformance
docker/docker-bench-securityPasses checked-in corpus conformancePasses checked-in corpus conformance
dockur/windowsPasses checked-in corpus conformancePasses checked-in corpus conformance
dokku/dokkuPasses checked-in corpus conformancePasses checked-in corpus conformance
dylanaraps/neofetchPasses checked-in corpus conformancePasses checked-in corpus conformance
dylanaraps/pure-bash-biblePasses checked-in corpus conformancePasses checked-in corpus conformance
dylanaraps/pure-sh-biblePasses checked-in corpus conformancePasses checked-in corpus conformance
fideloper/VaprobashPasses checked-in corpus conformancePasses checked-in corpus conformance
GameServerManagers/LinuxGSMPasses checked-in corpus conformancePasses checked-in corpus conformance
gentoo/gentoo2 known issues2 known issues
google/oss-fuzz2 known issues2 known issues
HariSekhon/DevOps-Bash-toolsPasses checked-in corpus conformancePasses checked-in corpus conformance
helmuthdu/auiPasses checked-in corpus conformancePasses checked-in corpus conformance
holman/dotfilesPasses checked-in corpus conformancePasses checked-in corpus conformance
hwdsl2/setup-ipsec-vpnPasses checked-in corpus conformancePasses checked-in corpus conformance
itzg/docker-minecraft-serverPasses checked-in corpus conformancePasses checked-in corpus conformance
jessfraz/dotfilesPasses checked-in corpus conformancePasses checked-in corpus conformance
jorgebucaran/fisherPasses checked-in corpus conformancePasses checked-in corpus conformance
juewuy/ShellCrash5 known issues5 known issues
ko1nksm/shellspec2 known issues2 known issues
kward/shunit21 known issue1 known issue
leebaird/discoverPasses checked-in corpus conformancePasses checked-in corpus conformance
lmc999/RegionRestrictionCheckPasses checked-in corpus conformancePasses checked-in corpus conformance
LukeSmithxyz/LARBSPasses checked-in corpus conformancePasses checked-in corpus conformance
masonr/yet-another-bench-script1 known issue1 known issue
mathiasbynens/dotfilesPasses checked-in corpus conformancePasses checked-in corpus conformance
megastep/makeself1 known issue1 known issue
moovweb/gvm1 known issue1 known issue
nextcloud/dockerPasses checked-in corpus conformancePasses checked-in corpus conformance
nvie/gitflowPasses checked-in corpus conformancePasses checked-in corpus conformance
nvm-sh/nvm2 known issues2 known issues
Nyr/openvpn-installPasses checked-in corpus conformancePasses checked-in corpus conformance
oh-my-fish/oh-my-fishPasses checked-in corpus conformancePasses checked-in corpus conformance
ohmyzsh/ohmyzsh1 known issue1 known issue
openrc/openrcPasses checked-in corpus conformancePasses checked-in corpus conformance
openvpn/easy-rsaPasses checked-in corpus conformancePasses checked-in corpus conformance
p8952/bockerPasses checked-in corpus conformancePasses checked-in corpus conformance
paulirish/dotfilesPasses checked-in corpus conformancePasses checked-in corpus conformance
pi-hole/pi-hole4 known issues4 known issues
pyenv/pyenvPasses checked-in corpus conformancePasses checked-in corpus conformance
pystardust/ani-cliPasses checked-in corpus conformancePasses checked-in corpus conformance
quickemu-project/quickemuPasses checked-in corpus conformancePasses checked-in corpus conformance
rbenv/rbenvPasses checked-in corpus conformancePasses checked-in corpus conformance
RetroPie/RetroPie-Setup1 known issue1 known issue
romkatv/powerlevel10kPasses checked-in corpus conformancePasses checked-in corpus conformance
rupa/zPasses checked-in corpus conformancePasses checked-in corpus conformance
rvm/rvm15 known issues15 known issues
scop/bash-completion1 known issue1 known issue
sickcodes/Docker-OSXPasses checked-in corpus conformancePasses checked-in corpus conformance
SlackBuildsOrg/slackbuilds2 known issues2 known issues
sorin-ionescu/preztoPasses checked-in corpus conformancePasses checked-in corpus conformance
spiritLHLS/one-click-installation-scriptPasses checked-in corpus conformancePasses checked-in corpus conformance
sstephenson/batsPasses checked-in corpus conformancePasses checked-in corpus conformance
super-linter/super-linter1 known issue1 known issue
swoodford/aws2 known issues2 known issues
termux/termux-packages2 known issues2 known issues
thoughtbot/dotfilesPasses checked-in corpus conformancePasses checked-in corpus conformance
tj/git-extrasPasses checked-in corpus conformancePasses checked-in corpus conformance
tj/nPasses checked-in corpus conformancePasses checked-in corpus conformance
tmux-plugins/tmux-resurrectPasses checked-in corpus conformancePasses checked-in corpus conformance
tmux-plugins/tpmPasses checked-in corpus conformancePasses checked-in corpus conformance
tteck/Proxmox15 known issues15 known issues
v1s1t0r1sh3r3/airgeddon2 known issues2 known issues
v2fly/fhs-install-v2rayPasses checked-in corpus conformancePasses checked-in corpus conformance
void-linux/void-packages1 known issue1 known issue
xwmx/nbPasses checked-in corpus conformancePasses checked-in corpus conformance
zdharma-continuum/zinitPasses checked-in corpus conformancePasses checked-in corpus conformance
zsh-users/zsh-autosuggestionsPasses checked-in corpus conformancePasses checked-in corpus conformance
zsh-users/zsh-completionsPasses checked-in corpus conformancePasses checked-in corpus conformance
zsh-users/zsh-syntax-highlightingPasses checked-in corpus conformancePasses checked-in corpus conformance