|
| 1 | +# sh |
| 2 | + |
| 3 | +[](https://godoc.org/mvdan.cc/sh) |
| 4 | +[](https://travis-ci.org/mvdan/sh) |
| 5 | +[](https://ci.appveyor.com/project/mvdan/sh) |
| 6 | +[](https://coveralls.io/github/mvdan/sh) |
| 7 | + |
| 8 | +A shell parser, formatter and interpreter. Supports [POSIX Shell], |
| 9 | +[Bash] and [mksh]. Requires Go 1.9 or later. |
| 10 | + |
| 11 | +### shfmt |
| 12 | + |
| 13 | + go get -u mvdan.cc/sh/cmd/shfmt |
| 14 | + |
| 15 | +`shfmt` formats shell programs. It can use tabs or any number of spaces |
| 16 | +to indent. See [canonical.sh](syntax/canonical.sh) for a quick look at |
| 17 | +its default style. |
| 18 | + |
| 19 | +You can feed it standard input, any number of files or any number of |
| 20 | +directories to recurse into. When recursing, it will operate on `.sh` |
| 21 | +and `.bash` files and ignore files starting with a period. It will also |
| 22 | +operate on files with no extension and a shell shebang. |
| 23 | + |
| 24 | + shfmt -l -w script.sh |
| 25 | + |
| 26 | +Use `-i N` to indent with a number of spaces instead of tabs. There are |
| 27 | +other formatting options - see `shfmt -h`. For example, to get the |
| 28 | +formatting appropriate for [Google's Style][google-style] guide, use |
| 29 | +`shfmt -i 2 -ci`. |
| 30 | + |
| 31 | +Packages are available for [Arch], [CRUX], [Homebrew], [NixOS] and [Void]. |
| 32 | + |
| 33 | +#### Advantages over `bash -n` |
| 34 | + |
| 35 | +`bash -n` can be useful to check for syntax errors in shell scripts. |
| 36 | +However, `shfmt >/dev/null` can do a better job as it checks for invalid |
| 37 | +UTF-8 and does all parsing statically, including checking POSIX Shell |
| 38 | +validity: |
| 39 | + |
| 40 | +``` |
| 41 | + $ echo '${foo:1 2}' | bash -n |
| 42 | + $ echo '${foo:1 2}' | shfmt |
| 43 | +1:9: not a valid arithmetic operator: 2 |
| 44 | + $ echo 'foo=(1 2)' | bash --posix -n |
| 45 | + $ echo 'foo=(1 2)' | shfmt -p |
| 46 | +1:5: arrays are a bash feature |
| 47 | +``` |
| 48 | + |
| 49 | +### gosh |
| 50 | + |
| 51 | + go get -u mvdan.cc/sh/cmd/gosh |
| 52 | + |
| 53 | +Experimental shell that uses `interp`. Work in progress, so don't expect |
| 54 | +stability just yet. |
| 55 | + |
| 56 | +### Fuzzing |
| 57 | + |
| 58 | +This project makes use of [go-fuzz] to find crashes and hangs in both |
| 59 | +the parser and the printer. To get started, run: |
| 60 | + |
| 61 | + git checkout fuzz |
| 62 | + ./fuzz |
| 63 | + |
| 64 | +### Caveats |
| 65 | + |
| 66 | +* Bash index expressions must be an arithmetic expression or a quoted |
| 67 | + string. This is because the static parser can't know whether the array |
| 68 | + is an associative array (string keys) since that depends on having |
| 69 | + called or not `declare -A`. |
| 70 | + |
| 71 | +``` |
| 72 | + $ echo '${array[spaced string]}' | shfmt |
| 73 | +1:16: not a valid arithmetic operator: string |
| 74 | +``` |
| 75 | + |
| 76 | +* `$((` and `((` ambiguity is not suported. Backtracking would greatly |
| 77 | + complicate the parser and make stream support - `io.Reader` - |
| 78 | + impossible. In practice, the POSIX spec recommends to [space the |
| 79 | + operands][posix-ambiguity] if `$( (` is meant. |
| 80 | + |
| 81 | +``` |
| 82 | + $ echo '$((foo); (bar))' | shfmt |
| 83 | +1:1: reached ) without matching $(( with )) |
| 84 | +``` |
| 85 | + |
| 86 | +* Some builtins like `export` and `let` are parsed as keywords. This is |
| 87 | + to allow statically parsing them and building their AST, as opposed to |
| 88 | + just keeping the arguments as a slice of arguments. |
| 89 | + |
| 90 | +### Related projects |
| 91 | + |
| 92 | +* [dockerised-shfmt] - A docker image of `shfmt` |
| 93 | +* [format-shell] - Atom plugin for `shfmt` |
| 94 | +* [micro] - Editor with a built-in plugin for `shfmt` |
| 95 | +* [shell-format] - VS Code plugin for `shfmt` |
| 96 | +* [vim-shfmt] - Vim plugin for `shfmt` |
| 97 | + |
| 98 | +[arch]: https://aur.archlinux.org/packages/shfmt/ |
| 99 | +[bash]: https://www.gnu.org/software/bash/ |
| 100 | +[crux]: https://github.com/6c37/crux-ports-git/tree/3.3/shfmt |
| 101 | +[dockerised-shfmt]: https://hub.docker.com/r/jamesmstone/shfmt/ |
| 102 | +[examples]: https://godoc.org/mvdan.cc/sh/syntax#pkg-examples |
| 103 | +[format-shell]: https://atom.io/packages/format-shell |
| 104 | +[go-fuzz]: https://github.com/dvyukov/go-fuzz |
| 105 | +[google-style]: https://google.github.io/styleguide/shell.xml |
| 106 | +[homebrew]: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/shfmt.rb |
| 107 | +[micro]: https://micro-editor.github.io/ |
| 108 | +[mksh]: https://www.mirbsd.org/mksh.htm |
| 109 | +[nixos]: https://github.com/NixOS/nixpkgs/blob/HEAD/pkgs/tools/text/shfmt/default.nix |
| 110 | +[posix shell]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
| 111 | +[posix-ambiguity]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_03 |
| 112 | +[shell-format]: https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format |
| 113 | +[vim-shfmt]: https://github.com/z0mbix/vim-shfmt |
| 114 | +[void]: https://github.com/voidlinux/void-packages/blob/HEAD/srcpkgs/shfmt/template |
0 commit comments