Skip to main content
Skip to main content

How to Lint your scripts

In this document, you'll learn how to lint argsh scripts using the built-in linter pipeline, which combines two complementary tools:

  1. shellcheck — general-purpose Bash linter (covers quoting, subshells, command substitution, POSIX portability, etc.).
  2. argsh-lint — argsh-specific static analysis (AG001–AG010, AG012–AG013; AG011 is reserved) for :args / :usage declarations, field specs, variable declarations, imports, and more.

Running argsh lint invokes both by default. Each can be selected individually via flags.

Requirements

  • shellcheck must be on PATH. If missing, argsh lint forwards the whole invocation to the argsh Docker image, which ships both linters pre-installed.
  • argsh-lint ships as a standalone binary. It is shipped in the argsh Docker image at /usr/local/bin/argsh-lint and published per-platform on the GitHub Releases page. If the binary is not found on PATH, argsh lint silently skips the argsh-specific checks (use --only-argsh to make that a hard error).

Usage

# Run both linters on auto-discovered files (via PATH_TESTS or project layout)
argsh lint

# Run on specific files / globs / directories
argsh lint scripts/*.sh hack/*.sh other-folder

# Run shellcheck only (skip argsh-specific diagnostics)
argsh lint --only-shellcheck scripts/

# Run argsh-lint only (skip shellcheck)
argsh lint --only-argsh scripts/

argsh lint exits non-zero if either linter reports diagnostics.

The two --only-* flags are mutually exclusive; passing both is a usage error (exit code 2).

Diagnostic Codes (argsh-lint)

argsh-lint reports diagnostics using stable AG#### codes, modeled after shellcheck's SC####. The full list:

CodeMeaning
AG001args array entry missing description
AG002usage array entry missing description
AG003Invalid field specification (unknown modifier, parse error)
AG004Array entry has no matching local <var> declaration
AG005args array declared but :args never called
AG006usage array declared but :usage never called
AG007usage target function cannot be resolved
AG008Duplicate flag name in the same args array
AG009Duplicate short alias in the same args array
AG010Command resolves to a bare (non-namespaced) function
AG012Local variable shadows a parent scope's args field
AG013import could not be resolved

Suppressing diagnostics

Add a comment directive, shellcheck-style:

# argsh disable=AG001,AG004       # next line only
# argsh disable-file=AG007 # entire file, one code
# argsh disable-file # entire file, all codes

The directive can appear anywhere in the file for file-level scope; for line-level scope it must be on the line directly preceding the diagnostic.

Using argsh-lint standalone

The underlying binary can be invoked directly for CI pipelines or editor integrations that don't drive it through argsh lint:

argsh-lint [OPTIONS] [FILE...]

Common flags (names chosen to match shellcheck):

FlagPurpose
-f FMT, --format=FMTOutput: gcc (default), tty, json, checkstyle, quiet
-e LIST, --exclude=LISTComma-separated codes to skip
-i LIST, --include=LISTOnly enable these codes
-S SEV, --severity=SEVMinimum severity: error, warning, info, style
-C WHEN, --color=WHENauto, always, never
--no-resolveSkip cross-file import resolution (faster)

Reading from stdin:

cat script.sh | argsh-lint --format=tty

Exit codes: 0 none, 1 diagnostics, 2 usage error.

VSCode Integration

Install the argsh VSCode extension to get the same diagnostics inline in your editor — it uses the argsh-lsp language server, which shares the exact analysis code with argsh-lint.

For generic shellcheck integration, install the shellcheck extension alongside.

Was this section helpful?