# Debugging, ShellCheck and Formatting — Bash / Shell Scripting

Source: https://www.skillbyai.com/en/bash/r-debug

> Find bugs with tracing and static analysis, and keep style consistent.

## See what Bash is actually doing

Most shell bugs are about what a line expands to. **`bash -x script.sh`** or **`set -x`** inside a script prints each command after expansion, prefixed by `+`, so you can see exactly which arguments a command received; `set +x` turns tracing off. Customise the prefix with **`PS4`**, for example `PS4='+ ${BASH_SOURCE##*/}:${LINENO}: '` to show file and line numbers. `bash -n script.sh` checks syntax without running anything. The single most valuable tool is **ShellCheck**, a static analyser that finds quoting bugs, unsafe `rm`, useless `cat`, `read` without `-r`, POSIX incompatibilities and many more problems, each with an explanation code (such as SC2086 for unquoted variables). Run it in your editor and in CI. **shfmt** formats scripts consistently. For larger scripts, **bats-core** provides a unit-testing framework for Bash functions and scripts.

## Tracing and linting

See expansions, then let ShellCheck catch the classic mistakes.

```bash
export PS4='+ ${BASH_SOURCE##*/}:${LINENO}: '
bash -x ./deploy.sh -e staging      # trace every expanded command

# inside a script, trace only a tricky section
set -x
rsync "${rsync_args[@]}" "$src" "$dest"
set +x

bash -n deploy.sh                   # syntax check only
shellcheck deploy.sh                # e.g. SC2086: double quote to prevent globbing and word splitting
shfmt -i 4 -w deploy.sh             # format with 4-space indentation

# CI step: fail the build on ShellCheck warnings
# shellcheck --severity=warning scripts/*.sh
```

## Make ShellCheck part of CI

ShellCheck catches in seconds the quoting bugs that otherwise surface months later on a file name with a space. Treat its warnings like compiler errors, and disable individual checks only with a comment explaining why.

**Quiz:** What does `set -x` do?

- [x] Prints each command after expansion before running it
- [ ] Exits on the first error
- [ ] Makes variables read-only
- [ ] Disables globbing

*Answer:* Prints each command after expansion before running it. Tracing shows the exact expanded commands, which is invaluable for debugging.
