shfmt Configuration
Master shfmt configuration for consistent shell script formatting across projects, including configuration files, EditorConfig integration, and team standardization.
Overview
shfmt is a shell parser, formatter, and interpreter that supports POSIX Shell, Bash, and mksh. Configuration allows you to define consistent formatting rules for your shell scripts.
Configuration Methods
shfmt supports multiple configuration approaches, in order of precedence:
-
Command-line flags (highest precedence)
-
EditorConfig settings
-
.shfmt.toml or shfmt.toml file
TOML Configuration File
Basic Configuration
Create .shfmt.toml or shfmt.toml in your project root:
Shell dialect (posix, bash, mksh, bats)
shell = "bash"
Indent with spaces (0 for tabs)
indent = 2
Binary operators at start of line
binary-next-line = true
Switch cases indented
switch-case-indent = true
Redirect operators followed by space
space-redirects = false
Keep column alignment paddings
keep-padding = false
Function opening brace on next line
func-next-line = false
Configuration Options Explained
shell
Specifies the shell dialect for parsing:
POSIX-compliant shell (most portable)
shell = "posix"
Bash (default for .bash files)
shell = "bash"
MirBSD Korn Shell
shell = "mksh"
Bats (Bash Automated Testing System)
shell = "bats"
Auto-detection based on shebang if not specified:
-
#!/bin/sh -> posix
-
#!/bin/bash or #!/usr/bin/env bash -> bash
-
#!/bin/mksh -> mksh
indent
Controls indentation style:
2 spaces (common)
indent = 2
4 spaces
indent = 4
Tabs (use 0)
indent = 0
binary-next-line
Controls binary operator positioning:
Operators at end of line (default)
binary-next-line = false
Result:
if [ "$a" = "foo" ] && [ "$b" = "bar" ]; then echo "match" fi
Operators at start of next line
binary-next-line = true
Result:
if [ "$a" = "foo" ]
&& [ "$b" = "bar" ]; then
echo "match"
fi
switch-case-indent
Controls case statement indentation:
Cases at same level as switch (default)
switch-case-indent = false
Result:
case "$1" in start) do_start ;; stop) do_stop ;; esac
Cases indented inside switch
switch-case-indent = true
Result:
case "$1" in start) do_start ;; stop) do_stop ;; esac
space-redirects
Controls spacing around redirections:
No space before redirect (default)
space-redirects = false
Result:
echo "hello" >file.txt cat <input.txt
Space before redirect
space-redirects = true
Result:
echo "hello" > file.txt cat < input.txt
keep-padding
Preserves alignment padding:
Remove extra alignment spaces (default)
keep-padding = false
Input with alignment:
short="value" longer_var="another"
Result (alignment removed):
short="value" longer_var="another"
Preserve alignment
keep-padding = true
Result (alignment kept):
short= "value" longer_var="another"
func-next-line
Controls function brace placement:
Opening brace on same line (default)
func-next-line = false
Result:
my_function() { echo "hello" }
Opening brace on next line
func-next-line = true
Result:
my_function() { echo "hello" }
EditorConfig Integration
shfmt respects EditorConfig settings. Create or update .editorconfig :
EditorConfig is awesome: https://EditorConfig.org
root = true
[*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true
[*.sh] indent_style = space indent_size = 2
shfmt-specific options
shell_variant = bash binary_next_line = true switch_case_indent = true space_redirects = false keep_padding = false function_next_line = false
EditorConfig Option Mapping
shfmt flag EditorConfig key
-i
indent_size (0 for tabs)
-ln
shell_variant
-bn
binary_next_line
-ci
switch_case_indent
-sr
space_redirects
-kp
keep_padding
-fn
function_next_line
Command-Line Flags
Basic Usage
Format file in place
shfmt -w script.sh
Show diff of changes
shfmt -d script.sh
List files that need formatting
shfmt -l .
Format and output to stdout
shfmt script.sh
Formatting Options
Set indent to 4 spaces
shfmt -i 4 script.sh
Use tabs for indentation
shfmt -i 0 script.sh
Specify shell dialect
shfmt -ln bash script.sh
Binary operators on next line
shfmt -bn script.sh
Indent switch cases
shfmt -ci script.sh
Space after redirects
shfmt -sr script.sh
Preserve padding
shfmt -kp script.sh
Function brace on next line
shfmt -fn script.sh
Combined Example
Common configuration
shfmt -i 2 -ci -bn -w script.sh
Project Configuration Patterns
Minimal Bash Project
.shfmt.toml
shell = "bash" indent = 2
POSIX-Compliant Scripts
.shfmt.toml
shell = "posix" indent = 4 binary-next-line = false switch-case-indent = false
Modern Bash with All Features
.shfmt.toml
shell = "bash" indent = 2 binary-next-line = true switch-case-indent = true space-redirects = false func-next-line = false
Team Standardization
.shfmt.toml - enforce team standards
shell = "bash" indent = 2 binary-next-line = true switch-case-indent = true keep-padding = false
CI/CD Integration
GitHub Actions
name: Shell Format Check user-invocable: false on: [push, pull_request] jobs: shfmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install shfmt run: | curl -sS https://webinstall.dev/shfmt | bash export PATH="$HOME/.local/bin:$PATH" - name: Check formatting run: shfmt -d .
Pre-commit Hook
.pre-commit-config.yaml
repos:
- repo: https://github.com/scop/pre-commit-shfmt
rev: v3.8.0-1
hooks:
- id: shfmt args: ["-w"]
Makefile Target
.PHONY: fmt-check fmt
fmt-check: shfmt -d .
fmt: shfmt -w .
Best Practices
-
Commit Configuration - Always commit .shfmt.toml for team consistency
-
Match Dialect - Set shell to match your script shebangs
-
EditorConfig First - Use EditorConfig for IDE integration
-
CI Enforcement - Run shfmt -d in CI to catch formatting issues
-
Consistent Indentation - Pick 2 or 4 spaces and stick with it
-
Document Choices - Add comments explaining non-default options
-
Version Lock - Pin shfmt version in CI for reproducibility
Troubleshooting
Configuration Not Applied
Check precedence order:
-
Command-line flags override everything
-
EditorConfig in file's directory
-
.shfmt.toml in project root
Wrong Shell Dialect
Verify shebang matches configuration:
If using shell = "bash", ensure scripts have:
#!/usr/bin/env bash
or
#!/bin/bash
EditorConfig Not Detected
Ensure EditorConfig file is in parent directory chain and has correct section:
[*.sh] shell_variant = bash
When to Use This Skill
-
Setting up shfmt in new projects
-
Configuring team-wide formatting standards
-
Integrating shfmt with CI/CD pipelines
-
Troubleshooting configuration issues
-
Migrating between configuration methods
-
Setting up EditorConfig for shell scripts