A shell language plugin for ESLint, providing:
- an ESLint-style parser for shell scripts (wrapping
mvdan-sh, the parser behind
shfmt) that produces an ESTree-style syntax tree, - an ESLint language implementation so rules can traverse that tree,
- rules inspired by ShellCheck checks, and
- a recommended configuration.
npm install --save-dev eslint @eslint/shellRequires Node.js ^20.19.0 || ^22.13.0 || >=24. Tested with ESLint v10.
Add the plugin to your eslint.config.js:
import shell from "@eslint/shell";
export default [
// use the recommended rules for *.sh and *.bash files
shell.configs.recommended,
];Or configure it manually:
import shell from "@eslint/shell";
export default [
{
files: ["**/*.sh"],
plugins: { shell },
language: "shell/bash",
rules: {
"shell/no-backticks": "error",
"shell/no-unquoted-expansions": "error",
},
},
];The plugin provides one language per shell dialect:
| Language | Description |
|---|---|
shell/bash |
Bash. Used by the recommended config. |
shell/posix |
POSIX sh, without Bash extensions. |
shell/mksh |
MirBSD Korn shell. |
export default [
{
files: ["**/*.sh"],
plugins: { shell },
language: "shell/posix",
rules: { "shell/no-backticks": "error" },
},
];Each rule mirrors a well-known ShellCheck check.
| Rule | ShellCheck | Description | Fixable | Recommended |
|---|---|---|---|---|
no-backticks |
SC2006 | Use $(...) instead of legacy backticks |
โ | error |
no-expansions-in-single-quotes |
SC2016 | Expressions don't expand in single quotes | warn | |
no-ls-iteration |
SC2045 | Don't iterate over ls output; use globs |
error | |
no-unquoted-expansions |
SC2086/46 | Quote expansions subject to word splitting and globbing | โ | error |
no-unused-vars |
SC2034 | Disallow variables that are assigned but never used | error | |
no-useless-cat |
SC2002 | Don't pipe from a single-file cat |
error | |
no-useless-echo |
SC2116 | Disallow $(echo ...) |
error | |
no-variables-in-printf-format |
SC2059 | Don't put variables in the printf format string |
error | |
require-cd-guard |
SC2164 | Handle cd failure with || exit (has suggestions) |
error | |
require-read-r |
SC2162 | Use read -r so backslashes aren't mangled |
โ | error |
shell/no-unused-vars accepts an object option:
"shell/no-unused-vars": ["error", { allowed: ["MY_GLOBAL"] }]Standard ESLint configuration comments work inside shell scripts:
# eslint-disable-next-line shell/no-backticks
echo `pwd`
echo `pwd` # eslint-disable-line shell/no-backticks -- legacy
# eslint shell/no-useless-echo: "warn"The tree format is documented in docs/syntax-tree.md.
Nodes carry start/end character offsets instead of range/loc
properties; use sourceCode.getRange(node) and sourceCode.getLoc(node).
The parser is also exported directly:
import { parseShell } from "@eslint/shell";
const { ast, comments } = parseShell('echo "hello"\n');npm install
npm test # unit + integration tests (Vitest)
npm run lint # ESLint on the plugin's own sources
npm run fmt # Prettier
npm run build # compile TypeScript to dist/Pre-commit hooks (via yorkie and lint-staged) run Prettier and ESLint on staged files. Releases are managed with release-please through GitHub Actions.
Apache-2.0