TopGit tracks mvdan/sh on GitHub as part of the Developer Tools family. The project has 9.0k stars. A shell parser, formatter, and interpreter with bash and zsh support; includes shfmt
Snapshot summary built from the project's own GitHub metadata — there's no written TopGit review yet. The page will update automatically when a full review is published.
WHY NO REVIEW YET
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then — see the READ ME tab for the original README in full.
A shell parser, formatter, and interpreter.
Supports POSIX Shell, Bash, Zsh, and mksh. Requires Go 1.25 or later.
Quick start
To parse shell scripts, inspect them, and print them out,
see the syntax package.
For high-level operations like performing shell expansions on strings,
see the shell package.
To interpret or run shell scripts,
see the interp package.
shfmt
go install mvdan.cc/sh/v3/cmd/shfmt@latest
shfmt formats shell programs. See canonical.sh for a
quick look at its default style. For example:
shfmt -l -w script.sh
For more information, see its manpage, which can be
viewed directly as Markdown or rendered with scdoc.
Packages are available on Alpine, Arch, Debian, Docker, Fedora, FreeBSD,
Homebrew, MacPorts, NixOS, OpenSUSE, Scoop, Snapcraft, Void and webi.
gosh
go install mvdan.cc/sh/v3/cmd/gosh@latest
Proof of concept shell that uses the interp package.
Fuzzing
We use Go's native fuzzing support. For instance:
cd syntax
go test -run=- -fuzz=ParsePrint
Caveats
When indexing Bash associative arrays, always use quotes. The static parser
will otherwise have to assume that the index is an arithmetic expression.
$ echo '${array[spaced string]}' | shfmt
<standard input>:1:16: not a valid arithmetic operator: `string`
$ echo '${array[weird!key]}' | shfmt
<standard input>:1:8: reached `!` without matching `[` with `]`
$ echo '${array[dash-string]}' | shfmt
${array[dash - string]}
$(( and (( ambiguity is not supported. Backtracking would complicate the
parser and make streaming support via io.Reader impossible. The POSIX spec
recommends to space the operands if $( ( is meant.
$ echo '$((foo); (bar))' | shfmt
1:1: reached ) without matching $(( with ))
export, let, and declare are parsed as keywords.
This allows statically building their syntax tree,
as opposed to keeping the arguments as a slice of words.
It is also required to support declare foo=(bar).
The entire library is written in pure Go, which limits how closely the
interpreter can follow POSIX Shell and Bash semantics.
For example, Go does not support forking its own process, so subshells
use a goroutine instead, meaning that real PIDs and file descriptors
cannot be used directly.
Formatting FAQs
The formatter cannot be disabled for ranges of lines; most users wanting this
are working around a bug or they don't like how a piece of code is formatted.
Instead, search the issue tracker and file a new issue if necessary.
Formatting of partial files leads to lots of edge cases and complexity
which this project has no resources for, nor interest in, getting into.
We avoid adding more formatting options where possible. Each added flag interacts
with all others, multiplying the human cost of development, maintenance, testing,
and properly documenting the behavior for end users.
The true value in a formatter is consistency, especially for teams of developers.
We do not aim to satisfy every developer's personal choice of optimal formatting.
JavaScript
The parser and formatter are available as a third party npm package called sh-syntax,
which bundles a version of this library compiled to WASM.
Previously, we maintained an npm package called mvdan-sh which used GopherJS
to bundle a JS version of this library. That npm package is now archived
given its poor performance and GopherJS not being as actively developed.
Any existing or new users should look at sh-syntax instead.
Docker
All release tags are published via Docker, such as v3.5.1.
The latest stable release is currently published as v3,
and the latest development version as latest.
The images only include shfmt; -alpine variants exist on Alpine Linux.
The most recent commit recorded on mvdan/sh was 20 days ago, based on the GitHub push timestamp. The repository has 434 forks — one of the better signals of community interest.
How does mvdan/sh compare to other Developer Tools projects?
mvdan/sh is tracked by TopGit in the Developer Tools category, with 9.0k GitHub stars and written in Go. Browse the Developer Tools topic page on TopGit to compare it against similar projects by stars and activity.
How many stars does mvdan/sh have?
mvdan/sh has 9.0k GitHub stars — refresh the page for the live number, or check github.com/mvdan/sh. TopGit mirrors GitHub's count but does not claim minute-by-minute accuracy.
Is mvdan/sh open source?
Yes — mvdan/sh ships under the BSD-3-Clause license, which makes its source code freely readable (and, depending on license terms, forkable and reusable). Source: github.com/mvdan/sh.
What is mvdan/sh?
mvdan/sh (mvdan/sh) is a Go project on GitHub. From the project's own README: A shell parser, formatter, and interpreter with bash and zsh support; includes shfmt
Where do I read more about mvdan/sh?
This TopGit page is a snapshot — the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/mvdan/sh is the definitive source.
Read full README in the tab above.
Curious whether sh is right for you?
Let ChatGPT, Claude, or Perplexity look into it — click below and see what AI actually says about sh.