Chapter 17Lesson 03~90 minutes

Formatting Bash with shfmt

Consistent formatting reduces review noise and makes shell control flow easier to scan. Formatting does not prove correctness, but it provides a stable base for linting and testing.

IntermediateTesting & debuggingHands-on lab

Learning objectives

By the end of this lesson

  • Check format without rewriting.
  • Apply one shared style.
  • Align editors and CI.
  • Handle generated and embedded content deliberately.
  • Control formatter version changes.

1. shfmt standardizes shell structure

shfmt parses shell source and rewrites it into a consistent style. This reduces formatting debates and makes control flow easier to review.

Formatting workflow
flowchart LR
  S["source"] --> P["shfmt parser"]
  P --> F["canonical format"]
  F --> D["diff or rewrite"]

2. Use diff mode in CI

shfmt -d scripts/

Diff mode checks formatting without changing repository files.

3. Use write mode locally

shfmt -w scripts/ci/build.sh

Developers can apply the standard automatically before committing.

4. Choose project flags once

SHFMT_FLAGS=(-i 2 -ci)

shfmt "${SHFMT_FLAGS[@]}" -d scripts/

Use the same options in editors, pre-commit helpers, and CI to prevent inconsistent results.

5. Formatting is not correctness

shfmt does not prove your variable expansions are safe or your business logic is correct. Combine it with ShellCheck and behavioral tests.

6. Here-documents deserve attention

cat <<'EOF'
this is data
  indentation may matter to the consumer
EOF

A formatter preserves shell semantics, but the data embedded in a here-document may have its own formatting rules.

7. Generated files need a consistent policy

Either format generated shell output automatically or exclude it explicitly. Do not mix manual formatting fixes into files that will be regenerated.

8. Formatter versions can create broad diffs

Control shfmt version changes so repository-wide formatting updates are intentional and easy to review.

9. Local hooks are convenience, CI is enforcement

Pre-commit formatting shortens feedback loops, but contributors can bypass hooks. CI should remain the authoritative formatting gate.

10. Formatting does not erase platform differences

A consistently formatted script can still depend on GNU-only utilities or Bash-only syntax. Portability remains a separate engineering concern.

11. Hands-on lab: format a messy script

mkdir -p "$HOME/devops-academy/bash/chapter17/lesson03"
cd "$HOME/devops-academy/bash/chapter17/lesson03"

cat > messy.sh <<'EOF'
#!/usr/bin/env bash
if [[ -n ${CI:-} ]];then
echo "CI mode"
else
  echo "local mode"
fi
for x in one two three;do echo "$x";done
EOF

if command -v shfmt >/dev/null 2>&1; then
  shfmt -i 2 -ci -d messy.sh || true
  shfmt -i 2 -ci -w messy.sh
  cat messy.sh
else
  printf 'shfmt not installed; inspect messy.sh manually.\n' >&2
fi

Verification checklist

12. Knowledge check

Question 1. What does shfmt -d do?

Question 2. Does shfmt replace ShellCheck?

Question 3. Why control formatter versions?

Question 4. Should CI verify formatting even when editors auto-format?

13. Summary

shfmt makes shell layout consistent and reviewable. Define one style, check it in CI, apply it locally, and treat it as a complement to linting and tests rather than a correctness tool.

14. Further reading

  • shfmt documentation.
  • mvdan/sh shell parser documentation.
  • GNU Bash Reference Manual — compound commands and here-documents.
  • ShellCheck documentation.
Next lesson

Unit and Integration Tests with Bats

Continue Chapter 17 by turning Bash quality and debugging practices into a repeatable engineering workflow.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.