sh
A shell parser, formatter, and interpreter. Supports POSIX Shell, Bash, and mksh. Requires Go 1.13 or later.
Quick start
To parse shell scripts, inspect them, and print them out, see the syntax examples.
For high-level operations like performing shell expansions on strings, see the shell examples.
shfmt
GO111MODULE=on go get mvdan.cc/sh/v3/cmd/shfmt
shfmt
formats shell programs. It can use tabs or any number of spaces to indent. See canonical.sh for a quick look at its default style.
You can feed it standard input, any number of files or any number of directories to recurse into. When recursing, it will operate on .sh
and .bash
files and ignore files starting with a period. It will also operate on files with no extension and a shell shebang.
shfmt -l -w script.sh
Typically, CI builds should use the command below, to error if any shell scripts in a project don't adhere to the format:
shfmt -d .
Use -i N
to indent with a number of spaces instead of tabs. There are other formatting options - see shfmt -h
. For example, to get the formatting appropriate for Google's Style guide, use shfmt -i 2 -ci
.
If any EditorConfig files are found, they will be used to apply formatting options. If any parser or printer flags are given to the tool, no EditorConfig files will be used. A default like -i=0
can be used for this purpose.
An example of the options available:
[*.sh]
# like -i=4
indent_style = space
indent_size = 4
shell_variant = posix # like -ln=posix
binary_next_line = true # like -bn
switch_case_indent = true # like -ci
space_redirects = true # like -sr
keep_padding = true # like -kp
function_next_line = true # like -fn
# Ignore the entire "third_party" directory.
[third_party/**]
ignore = true
Packages are available on Alpine, Arch, Docker, FreeBSD, Homebrew, MacPorts, NixOS, Scoop, Snapcraft, and Void.
bash -n
Replacing bash -n
can be useful to check for syntax errors in shell scripts. However, shfmt >/dev/null
can do a better job as it checks for invalid UTF-8 and does all parsing statically, including checking POSIX Shell validity:
$ echo '${foo:1 2}' | bash -n
$ echo '${foo:1 2}' | shfmt
1:9: not a valid arithmetic operator: 2
$ echo 'foo=(1 2)' | bash --posix -n
$ echo 'foo=(1 2)' | shfmt -p
1:5: arrays are a bash feature
gosh
GO111MODULE=on go get mvdan.cc/sh/v3/cmd/gosh
Proof of concept shell that uses interp
. Note that it's not meant to replace a POSIX shell at the moment, and its options are intentionally minimalistic.
Fuzzing
This project makes use of go-fuzz to find crashes and hangs in both the parser and the printer. To get started, run:
git checkout fuzz
./fuzz
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
1:16: not a valid arithmetic operator: string
$ echo '${array[dash-string]}' | shfmt
${array[dash - string]}
$((
and((
ambiguity is not supported. Backtracking would complicate the parser and make streaming support viaio.Reader
impossible. The POSIX spec recommends to space the operands if$( (
is meant.
$ echo '$((foo); (bar))' | shfmt
1:1: reached ) without matching $(( with ))
- Some builtins like
export
andlet
are parsed as keywords. This is to allow statically parsing them and building their syntax tree, as opposed to just keeping the arguments as a slice of arguments.
JavaScript
A subset of the Go packages are available as an npm package called mvdan-sh. See the _js directory for more information.
Docker
To build a Docker image, checkout a specific version of the repository and run:
docker build -t my:tag -f cmd/shfmt/Dockerfile .
This creates an image that only includes shfmt. Alternatively, if you want an image that includes alpine, add --target alpine
. To use the Docker image, run:
docker run --rm -v $PWD:/mnt -w /mnt my:tag <shfmt arguments>
pre-commit
It is possible to use shfmt with pre-commit and a local
repo configuration like:
- repo: local
hooks:
- id: shfmt
name: shfmt
minimum_pre_commit_version: 2.4.0
language: golang
additional_dependencies: [mvdan.cc/sh/v3/cmd/[email protected]]
entry: shfmt
args: [-w]
types: [shell]
Related projects
- Alternative docker images - by jamesmstone, PeterDaveHello
- format-shell - Atom plugin for
shfmt
- micro - Editor with a built-in plugin for
shfmt
- modd - A developer tool that responds to filesystem changes, using
sh
- prettier-plugin-sh - Prettier plugin using mvdan-sh
- sh-checker - A GitHub Action that performs static analysis for shell scripts using shfmt and shellcheck
- shell-format - VS Code plugin for
shfmt
- shfmt.el - Emacs package for
shfmt
- Sublime-Pretty-Shell - Sublime Text 3 plugin for
shfmt
- vim-shfmt - Vim plugin for
shfmt