Skip to main content

beautify_bash

CI PyPI

A code formatter / beautifier for bash and zsh shell scripts.

Originally written in Ruby, then Python, by Paul Lutus; revived here with a proper package layout, a Typer CLI, uv tooling, a test suite, and support for shell dialects.

Install

uv tool install beautify-bash      # as a standalone command
uv add beautify-bash               # as a project dependency
pipx install beautify-bash         # or with pipx

Run it without installing:

uvx beautify-bash script.sh

Requires Python 3.9 or newer (tested through 3.14).

Use

Like shfmt and gofmt, it prints to standard output and changes nothing on disk unless you ask it to:

beautify-bash script.sh                 # print the formatted script
beautify-bash -w script.sh              # rewrite in place, keeping script.sh~
beautify-bash -w --no-backup script.sh  # rewrite without a backup
beautify-bash -o tidy.sh script.sh      # write the result to another file
beautify-bash --check src/*.sh          # exit 1 if anything would change (CI)
beautify-bash --diff script.sh          # show a unified diff, write nothing
beautify-bash --indent 4 script.sh      # four spaces per level
beautify-bash --tabs script.sh          # indent with tabs
beautify-bash -d zsh script.zsh         # force a dialect
cat script.sh | beautify-bash -         # stdin to stdout
beautify-bash -w src/*.sh               # format a whole directory
python -m beautify_bash script.sh       # same thing via the module

Standard output carries only the formatted script; progress notes, diffs of the --check kind, and errors go to standard error, so beautify-bash x.sh > y.sh is safe.

Options

Option Meaning
(none) Print the formatted script to stdout; several inputs are concatenated.
-w, --write Rewrite each input file in place.
-o, --output PATH Write the result to PATH (- means stdout).
-i, --indent N Spaces per indentation level (default 2).
--tabs / --spaces Indent with tab characters instead of spaces.
-d, --dialect auto (default), bash, or zsh.
--check Write nothing; exit 1 if a file would change.
--diff Print a unified diff instead of writing.
--backup / --no-backup With -w, keep the original as FILE~ (default on).
-q, --quiet Suppress per-file progress notes.
-V, --version Print the version and exit.

-w, -o, --check and --diff are mutually exclusive, and -w cannot rewrite standard input.

Exit codes: 0 clean, 1 a file changed (under --check/--diff) or a syntax problem was reported, 2 a file could not be read or written, or the options conflict.

Dialects

--dialect auto picks the dialect from the shebang line first, then the file name (.zsh, .zshrc, .zshenv, …), and falls back to bash.

Dialect Block keywords
bash case/esac, if/then/elif/else/fi, do/done, {}, (), []
zsh everything bash has, plus foreach … end and } always {

Adding a shell means adding one Dialect instance in dialects.py — the indenter itself is dialect agnostic.

Library use

from beautify_bash import ZSH, Beautifier, beautify_string

result = beautify_string(open("script.sh").read())
print(result.text)
print(result.dialect.name, result.ok, result.errors)

# Explicit configuration
tidy = Beautifier(indent_char="\t", indent_size=1, dialect=ZSH)
print(tidy.format("foreach f (a b)\nprint $f\nend").text)

The 1.x API (BeautifyBash, beautify_string(data, path) -> (text, error), beautify_file, tab_str, tab_size) still works.

What is preserved verbatim

Re-indenting must never change what a script does, so these regions are passed through byte for byte:

  • here-document bodies (<<EOF, and <<-EOF with its indented terminator),
  • lines inside a multi-line '…' or "…" string,
  • everything inside comments and quotes is ignored for keyword counting.

Shell reserved words only count as keywords in command position — at the start of a line or after ;, &, |, or a bracket — so echo done no longer dedents the following lines.

Pre-commit

repos:
  - repo: local
    hooks:
      - id: beautify-bash
        name: beautify-bash
        entry: beautify-bash -w
        language: system
        types: [shell]

Development

uv sync                     # create .venv and install dev dependencies
uv run pytest               # run the tests
uv run pytest --cov         # with coverage
uv run ruff check .         # lint
uv run ruff format .        # format the Python sources
uv run mypy                 # type check

The test suite is in tests/:

Roadmap

See TODO.md.

License

GPL-2.0-or-later. See LICENSE.

Metadata

Release files for beautify-bash 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for beautify-bash 2.0.0
File Size Uploaded Provenance Actions
beautify_bash-2.0.0.tar.gz 92.1 kB Without provenance Details for beautify_bash-2.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for beautify-bash 2.0.0
File Interpreter ABI Platform Actions
beautify_bash-2.0.0-py3-none-any.whl Python 3 none any Details for beautify_bash-2.0.0-py3-none-any.whl

Total release size: 112.8 kB

Release files / beautify_bash-2.0.0.tar.gz

Download URL beautify_bash-2.0.0.tar.gz
Size 92.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d9eaef33f8c7a967fc52ce90aaf235b84b5df3049db579c64ef3f0ffe670d0a9
BLAKE2b-256 checksum
How to use checksums
e4e5caef231235646d08a4d47622ba7d73b1736247bc250fbca4dcbc1e0c1d61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / beautify_bash-2.0.0-py3-none-any.whl

Download URL beautify_bash-2.0.0-py3-none-any.whl
Size 20.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
db64a72d645226a220cfd84b52f6baa1bd4fb63f00af7c04d4822f46e8b3d385
BLAKE2b-256 checksum
How to use checksums
d9dec7ff8b3977481bd7b7cd656b24856a54162fa772f7dcb26aa537c63c8f58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for beautify-bash 2.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page