GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - mvdan/sh: A shell parser, formatter, and interpreter with bash and zsh support; includes shfmt

BranchesTags
Open more actions menu

Latest commit

 

History

4,531 Commits
4,531 Commits

Folders and files

NameName
Last commit message
Last commit date
.github
.github
 
 
cmd
cmd
 
 
doc
doc
 
 
expand
expand
 
 
fileutil
fileutil
 
 
internal
internal
 
 
interp
interp
 
 
pattern
pattern
 
 
shell
shell
 
 
syntax
syntax
 
 
x
x
 
 
.gitattributes
.gitattributes
 
 
.gitignore
.gitignore
 
 
AGENTS.md
AGENTS.md
 
 
CHANGELOG.md
CHANGELOG.md
 
 
CLAUDE.md
CLAUDE.md
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
go.mod
go.mod
 
 
go.sum
go.sum
 
 

Repository files navigation

sh

Go Reference

A shell parser, formatter, and interpreter. Supports POSIX Shell, Bash, Zsh, and mksh. Requires Go 1.26 or later.

Quick start

To parse shell scripts into a syntax tree, inspect them, and format them like shfmt does, see the syntax package.

For one-call helpers with shell semantics, see the shell package: split a command line into arguments and quote them back like shlex, expand $VAR and ~ in strings, and glob with ** or match case-style patterns.

To run shell scripts without a system shell, including on Windows, see the interp package; its handlers allow configuring what scripts can execute and access.

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, PyPI, Scoop, Snapcraft, Void and webi.

Sponsoring

If this project saves you or your company time, consider sponsoring me on GitHub. Monthly tiers include benefits like your logo on a README, prioritized issues, or direct support in your company's chat app. One-time tiers offer a call about one of my projects or a Go consulting or mentorship session.

CI powered by Namespace

Contributing

Bug reports and feature requests should be filed as detailed issues, ideally with an example which reproduces the bug or shows what feature you're after.

Unless you're an active user or contributor to the project, drive-by AI patches are not helpful. File detailed issues instead.

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]}

This does not apply to Zsh, which only decides at run time whether a subscript is a key or arithmetic, so the parser keeps subscripts as written.

  • $(( 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 ))
  • Unlike Bash, arithmetic expressions are parsed before their words are expanded, so $((1 ? 1 : $(cmd))) never runs cmd, with a='1 + 2' $(($a * 2)) is 6 rather than 5, and $((1 $op 2)) is a syntax error.

  • 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.

To build a Docker image, run:

docker build -t my:tag -f cmd/shfmt/Dockerfile .

To use a Docker image, run:

docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/mnt" -w /mnt my:tag <shfmt arguments>

Related projects

The following editor integrations wrap shfmt:

  • BashSupport-Pro - Bash plugin for JetBrains IDEs
  • dockerfmt - Dockerfile formatter using shfmt
  • intellij-shellscript - Intellij Jetbrains shell script plugin
  • micro - Editor with a built-in plugin
  • neoformat - (Neo)Vim plugin
  • vscode-shfmt - VS Code plugin
  • shfmt.el - Emacs package
  • Trunk - Universal linter, available as a CLI, VS Code plugin, and GitHub action

Other noteworthy integrations include:

  • modd - A developer tool that responds to filesystem changes
  • prettier-plugin-sh - Prettier plugin using sh-syntax
  • sh-checker - A GitHub Action that performs static analysis for shell scripts
  • mdformat-shfmt - mdformat plugin to format shell scripts embedded in Markdown with shfmt
  • pre-commit-shfmt - pre-commit shfmt hook
  • tesh - Run scripts with mocks, assertions, and coverage

About

A shell parser, formatter, and interpreter with bash and zsh support; includes shfmt

pkg.go.dev/mvdan.cc/sh/v3

Topics

bashbeautifyformattergointerpretermkshparserposixshellshfmtzsh

Resources

Readme
BSD-3-Clause license
Activity

Stars

9.2k stars

Watchers

57 watching

Forks

463 forks
Report repository

Releases

Sponsor this project

Used by

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.