Lint shell scripts as you type with ShellCheck, with quick fixes and nothing else to install.
- Reports ShellCheck's warnings in the editor and the Problems view, as you type or on save.
- Quick fixes for single warnings, and Fix All for the whole file.
- Bundles ShellCheck, so there is no binary to install on common platforms.
- Works on vscode.dev and github.dev through a bundled WebAssembly build.
- Reads your
.shellcheckrc, like theshellcheckcommand line does.
Open a shell script. ShellCheck checks it as you type, underlines each problem, and lists it in the Problems view. Hover a problem for its rule code and a link to the rule's documentation, or use the light bulb to apply a fix.
The ShellCheck icon on the right of the status bar opens a menu to lint the current document, change when ShellCheck runs, or turn it off for the workspace. The same actions are in the Command Palette:
| Command | What it does |
|---|---|
| ShellCheck: Lint Current Document | Lints the active script now. |
| ShellCheck: Show Menu | Opens the status bar menu. |
| ShellCheck: Collect Diagnostics For Current Document | Opens a troubleshooting report for the active script. |
| ShellCheck: Use the Experimental WebAssembly Runtime | Switches to the WebAssembly runtime. |
To apply every automatic fix on save:
To do it on demand instead, run Fix All from the Command Palette.
Install ShellCheck from the Visual Studio Marketplace, or from Open VSX in VSCodium and other editors that use it. From Quick Open (Ctrl+P, or Cmd+P on macOS):
ext install timonwong.shellcheck
Or from a terminal:
code --install-extension timonwong.shellcheckThe extension bundles precompiled ShellCheck binaries for:
- Linux (
x86_64,arm64,arm) - macOS (
x86_64,arm64) - Windows (
x86_64,arm64with thex86_64binary)
On other platforms, it runs the shellcheck found on your PATH. To use a different binary, set shellcheck.executablePath; it takes priority over the bundled one. In Restricted Mode, only the value from your user settings is used.
A WebAssembly build of ShellCheck is bundled for every platform as well; see Experimental WebAssembly runtime.
| Setting | Default | Description |
|---|---|---|
shellcheck.enable |
true |
Turns ShellCheck on or off. |
shellcheck.run |
"onType" |
When to lint: "onType", "onSave", or "manual" (only the Lint Current Document command). |
shellcheck.enableQuickFix |
true |
Offers quick fixes. |
shellcheck.exclude |
[] |
Rule codes to ignore, such as "1017". Prefer .shellcheckrc. |
shellcheck.customArgs |
[] |
Extra arguments for ShellCheck. Prefer .shellcheckrc. |
shellcheck.ignorePatterns |
csh, fish, tcsh, xonsh, zsh files | Files not to lint. See below. |
shellcheck.ignoreFileSchemes |
Git and pull request views | URI schemes not to lint, such as git and review. |
shellcheck.markdownDiagnostics |
false |
Experimental formatted hover. See below. |
shellcheck.watchConfigFiles.workspace |
false |
Lints open scripts again when a .shellcheckrc in the workspace changes. |
shellcheck.watchConfigFiles.user |
false |
The same, for config files outside the workspace, such as ~/.shellcheckrc. Native runtime only. |
shellcheck.runtime |
"native" |
"native" or the experimental "wasm". |
shellcheck.executablePath |
bundled | Path to the shellcheck executable. |
shellcheck.useWorkspaceRootAsCwd |
false |
Runs ShellCheck from the workspace root instead of the script's directory. |
shellcheck.runTimeout |
0 |
Seconds before a native run is stopped; 0 for no limit. |
shellcheck.maxConcurrentRuns |
0 |
Most native runs at once; 0 for no limit. |
shellcheck.disableVersionCheck |
false |
Stops prompting to update an outdated shellcheck binary. |
shellcheck.logLevel |
"info" |
Level of the extension's log. |
The Settings editor shows the full default lists.
ShellCheck has a default set of checks, but it is also configurable using RC files. To configure your project, add a .shellcheckrc at the workspace root. If no .shellcheckrc is found in any of the parent directories, ShellCheck will look in ~/.shellcheckrc followed by the $XDG_CONFIG_HOME (usually ~/.config/shellcheckrc) on Unix, or %APPDATA%/shellcheckrc on Windows. Only the first file found will be used.
Here is an example .shellcheckrc:
# Look for 'source'd files relative to the checked script,
# and also look for absolute paths in /mnt/chroot
source-path=SCRIPTDIR
source-path=/mnt/chroot
# Since ShellCheck 0.9.0, values can be quoted with '' or "" to allow spaces
source-path="My Documents/scripts"
# Allow opening any 'source'd file, even if not specified as input
external-sources=true
# Turn on warnings for unquoted variables with safe values
enable=quote-safe-variables
# Turn on warnings for unassigned uppercase variables
enable=check-unassigned-uppercase
# Allow [ ! -z foo ] instead of suggesting -n
disable=SC2236By default, a changed .shellcheckrc applies to a script the next time it is linted. To lint open scripts again right away, turn on shellcheck.watchConfigFiles.workspace, and shellcheck.watchConfigFiles.user for config files outside the workspace.
As a last resort, you can also add rule codes to shellcheck.exclude. For example, to exclude SC1017:
{
"shellcheck.exclude": ["1017"]
}Each key of shellcheck.ignorePatterns is a picomatch glob pattern, matched against the file's path relative to its workspace folder. Files outside every workspace folder are matched by their absolute path, so start patterns with **/. Wildcards also match dot files, and extglob patterns such as !(…) are supported.
For example:
{
"shellcheck.ignorePatterns": {
"**/*.zsh": true,
"**/*.zsh*": true,
"**/.git/*.sh": true,
"**/folder/**/*.sh": true
}
}To skip files without an extension (dot files such as .bashrc count as having one):
{
"shellcheck.ignorePatterns": {
"**/!(*.*)": true
}
}Your patterns are merged with the defaults, so list only the ones you add or change. Set a default pattern to false to lint those files again:
{
"shellcheck.ignorePatterns": {
"**/bin/**": true,
"**/*.fish": false
}
}shellcheck.markdownDiagnostics is experimental. Enable it to show a formatted hover with a colored severity, a muted rule code, and a link to the rule documentation. Colors follow the active editor theme. Common ShellCheck code examples in diagnostic messages are rendered as Markdown code spans; messages remain plain text in the Problems view.
VS Code displays the original diagnostic alongside the formatted hover. To hide the original ShellCheck diagnostics and put the formatted hover first:
- Install Custom CSS and JS Loader.
- Follow the installation instructions provided by that extension.
- Load
doc/markdown-diagnostics.css.
Only ShellCheck's original diagnostics are hidden; diagnostics from other sources and the Quick Fix / View Problem actions stay visible below the formatted hover.
VS Code currently does not expose an extension API for ordering hover providers or replacing the native diagnostic hover. This formatted-hover implementation follows the approach used by Pretty TypeScript Errors, including the internal marker and CSS workaround that hides the native rows and moves the formatted row first. Read more about the workaround.
Messages are formatted only when you hover, and not at all while the setting is off.
Changing a setting or a .shellcheckrc lints every open script again, which starts one shellcheck process per script. With many scripts open, set shellcheck.maxConcurrentRuns to cap how many run at once; the others wait their turn. The default, 0, sets no limit. A run that never finishes keeps its slot, so pair this with shellcheck.runTimeout. Only the native runtime is affected: the WebAssembly runtime always lints one script at a time.
{
"shellcheck.maxConcurrentRuns": 4
}Point shellcheck.executablePath at a shim script that runs ShellCheck in a container. Keep ShellCheck arguments out of the script.
Here is a simple shim script to get started with (see discussion: #24):
#!/bin/bash
exec docker run --rm -i -v "${PWD}:${PWD}:ro" -w "${PWD}" koalaman/shellcheck:latest "$@"For example, place it at shellcheck.sh in the root of your workspace with execution permission (chmod +x shellcheck.sh), then configure the extension to use it:
// .vscode/settings.json
{
// use the shim as shellcheck executable
"shellcheck.executablePath": "${workspaceFolder}/shellcheck.sh",
// you may also need to turn this option on, so shellcheck in the container
// can access all the files in the workspace and not only the directory
// where the file being linted is.
"shellcheck.useWorkspaceRootAsCwd": true
}Starting a container is slower than running the binary, so expect each lint to take longer.
A WebAssembly build of ShellCheck is bundled in this extension and can check your scripts on its own. It needs no shellcheck executable on your machine, and it works on every platform, including those with no prebuilt ShellCheck binary. It reads .shellcheckrc and source targets through VS Code rather than from disk, so it also checks scripts in virtual workspaces and on any other file system VS Code can open; the native runtime cannot lint documents of a virtual workspace.
To turn it on:
{
"shellcheck.runtime": "wasm" // also: "native", the default
}This runtime is experimental and unsupported. It never falls back to the native binary: if it fails to start or a check crashes, your scripts stop being checked until you switch back. The failure is reported once per session, with the actions Switch back to native and Show Log.
It is also 3-4x slower than the native binary, and linting as you type correspondingly waits longer after your last keystroke. For large files, consider setting shellcheck.run to onSave.
Known limitations:
shellcheck.executablePathis ignored.- Only files inside the document's workspace folder are readable, so
sourcetargets and.shellcheckrcfiles outside that folder are not found. A file that belongs to no workspace folder sees only its own directory, and an untitled document sees no files at all. - Symbolic links inside that folder are followed wherever they lead.
- Path-like entries in
shellcheck.customArgsare passed through unchanged. They name locations on your machine, which this runtime does not see under those names, so they will not resolve.
The extension works on vscode.dev and github.dev, where it always uses the WebAssembly runtime. A self-hosted VS Code for the Web must be cross-origin isolated by sending COOP same-origin and COEP require-corp headers.
Supported browsers are Chrome and Edge 112 or later, Firefox 121 or later, and Safari 18.2 or later (macOS 13 or later, iOS and iPadOS 18.2 or later). Safari 16.4 to 18.1 and Firefox ESR 115 are not supported: they lack WebAssembly tail calls, and the extension reports that instead of linting.
.shellcheckrc and source paths resolve within the document's workspace folder, as they do with the desktop WebAssembly runtime.
If ShellCheck reports nothing or seems stuck, run ShellCheck: Collect Diagnostics For Current Document from the Command Palette with the script open. It opens a report showing which ShellCheck runs, its version, and why the script may be skipped, such as an ignore pattern or an ignored file scheme. Attach it when you open an issue.
This extension provides a small API, which allows other VS Code extensions to interact with the ShellCheck extension. For details, see API.md.
To build, test, or translate the extension, see CONTRIBUTING.md.
This extension was originally based on @hoovercj's Haskell Linter.
This extension is licensed under the MIT license.
The bundled ShellCheck binaries are licensed under GPLv3.
The WebAssembly build of ShellCheck ships as the separate @vscode-shellcheck/shellcheck-wasm package under GPL-3.0-or-later, except for its client entry, which is MIT.

{ "editor.codeActionsOnSave": { "source.fixAll.shellcheck": "explicit" } }