Loading...
Loading...
Use when formatting shell scripts with shfmt. Covers consistent formatting patterns, shell dialect support, common issues, and editor integration.
npx skill4agent add thebushidocollective/han shfmt-formatting/bin/sh#!/bin/sh
# POSIX-compliant syntax only
# No arrays
# No [[ ]] tests
# No $() must work in all contexts
if [ "$var" = "value" ]; then
echo "match"
fi#!/usr/bin/env bash
# Bash-specific features allowed
declare -a array=("one" "two" "three")
if [[ "$var" == "value" ]]; then
echo "match"
fi
result=$((1 + 2))#!/bin/mksh
# mksh-specific syntax
typeset -A assoc
assoc[key]=value#!/usr/bin/env bats
@test "example test" {
run my_command
[ "$status" -eq 0 ]
}if [ "$x" = "y" ]; then
echo "two spaces"
echo "four spaces"
echo "tab"
fi-i 2if [ "$x" = "y" ]; then
echo "two spaces"
echo "four spaces"
echo "tab"
fiif[$x="y"];then
echo "no spaces"
fi
x=1;y=2;z=3if [ $x = "y" ]; then
echo "no spaces"
fi
x=1
y=2
z=3if [ "$x" ]; then echo "yes"; else echo "no"; fiif [ "$x" ]; then
echo "yes"
else
echo "no"
ficat <<EOF
line 1
line 2
EOFcat <<EOF
line 1
line 2
EOF<<-if true; then
cat <<-EOF
indented content
more content
EOF
fifunction my_func
{
echo "old style"
}
my_func2 () { echo "one liner"; }function my_func {
echo "old style"
}
my_func2() {
echo "one liner"
}-fnfunction my_func
{
echo "brace on new line"
}case "$1" in
start) do_start;;
stop)
do_stop
;;
*) echo "unknown";;
esaccase "$1" in
start)
do_start
;;
stop)
do_stop
;;
*)
echo "unknown"
;;
esac-cicase "$1" in
start)
do_start
;;
stop)
do_stop
;;
esacif [ "$a" = "foo" ] &&
[ "$b" = "bar" ]; then
echo "match"
fi-bnif [ "$a" = "foo" ] \
&& [ "$b" = "bar" ]; then
echo "match"
fiecho "hello" >file.txt
cat <input.txt 2>&1-srecho "hello" > file.txt
cat < input.txt 2>&1# Convert all to spaces (2-space indent)
shfmt -i 2 -w script.sh
# Or convert all to tabs
shfmt -i 0 -w script.shecho "hello";
x=1;echo "hello"
x=1echo 'single' "double" $'ansi'
x="simple"echo 'single' "double" $'ansi'
x="simple"# Long command with continuation
very_long_command \
--option1 value1 \
--option2 value2 \
--option3 value3# Single line (preserved)
array=(one two three)
# Multi-line (preserved)
array=(
one
two
three
)// settings.json
{
"shellformat.path": "/usr/local/bin/shfmt",
"shellformat.flag": "-i 2 -ci -bn",
"[shellscript]": {
"editor.defaultFormatter": "foxundermoon.shell-format",
"editor.formatOnSave": true
}
}" .vimrc
let g:ale_fixers = {
\ 'sh': ['shfmt'],
\}
let g:ale_sh_shfmt_options = '-i 2 -ci -bn'
let g:ale_fix_on_save = 1" .vimrc
autocmd FileType sh setlocal formatprg=shfmt\ -i\ 2\ -ci;; init.el
(use-package reformatter
:config
(reformatter-define shfmt
:program "shfmt"
:args '("-i" "2" "-ci")))
(add-hook 'sh-mode-hook 'shfmt-on-save-mode)Path to shfmt: /usr/local/bin/shfmt
Options: -i 2 -ci -bn# Show diff of what would change (exit 1 if changes needed)
shfmt -d script.sh
shfmt -d .
# List files that need formatting
shfmt -l .
# Exit codes:
# 0 = no changes needed
# 1 = changes needed or error# Overwrite files with formatted version
shfmt -w script.sh
shfmt -w .# Output formatted version to stdout
shfmt script.sh
# Output formatted version to file
shfmt script.sh > formatted.sh# Format only staged shell scripts
git diff --cached --name-only --diff-filter=ACM | \
grep '\.sh$' | \
xargs -r shfmt -w#!/usr/bin/env bash
# .git/hooks/pre-commit
# Check if shfmt is available
if ! command -v shfmt &>/dev/null; then
echo "shfmt not found, skipping format check"
exit 0
fi
# Get staged shell files
files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.sh$')
if [ -n "$files" ]; then
# Check formatting
if ! echo "$files" | xargs shfmt -d; then
echo "Shell scripts need formatting. Run: shfmt -w <files>"
exit 1
fi
fi# Format files changed since main branch
git diff --name-only main...HEAD | \
grep '\.sh$' | \
xargs -r shfmt -w# Minify script
shfmt -mn script.sh > script.min.sh#!/bin/bash
# Comment
function hello {
echo "Hello, World!"
}
hello#!/bin/bash
function hello { echo "Hello, World!"; }
helloshfmt -d.shfmt.toml# Check syntax first
bash -n script.sh
# Or for POSIX
sh -n script.shshfmt -ln bash script.sh
shfmt -ln posix script.sh# shfmt:ignore