Bash Style Guide¶
What's on this page
- Core content for this topic in the Inkorporated enterprise OS.
- Practical guidance, roles, or standards as applicable.
What this enables
- Consistent enterprise operations and decision-making.
- Shared language for humans and cyborg AI agents.
This document outlines the coding standards and best practices for Bash scripting within this project. Adhering to these guidelines ensures code consistency, robustness, portability, and readability.
Table of Contents¶
- General Principles
- File Header and Extension
- Formatting and Indentation
- Naming Conventions
- Variables and Parameters
- Input and Output
- Control Flow
- Functions
- Command Substitution
- Error Handling and Robustness
- Comments and Documentation
- Security
- Clean Up and Signals
- Recommended Tools
General Principles¶
- ShellCheck Clean: All scripts must pass
shellcheckwithout warnings. - Robustness: Scripts should fail early and loudly. Avoid silent failures.
- Portability: Prefer standard Bash features. If a script is meant to be POSIX sh compliant, ensure it is. However, this guide assumes Bash is the target.
- Readability: Write code that is easy to understand. Descriptive variable names and comments are crucial.
- Idempotency: Whenever possible, scripts should be idempotent (safe to run multiple times).
File Header and Extension¶
- Shebang: Always start with a shebang. Use
#!/usr/bin/env bashfor portability, rather than#!/bin/bash. - Extension: Executable scripts should preferably not have an extension (e.g.,
my-script). Library files (not executable, intended to be sourced) should end in.shor.bash. - Mode: Executable scripts must have the executable bit set (
chmod +x).
Formatting and Indentation¶
- Indentation: Use 2 spaces for indentation. Do not use tabs.
- Line Length: Aim for 80 characters, but 100 is acceptable if it improves readability.
- Pipelines: Break long pipelines into multiple lines with the pipe
|at the end of the line. Indent the next line. - Blocks: Put
then,doon the same line as the condition/loop. - Blank Lines: Use blank lines to group logical blocks of code.
# Good
if [[ "$condition" == "true" ]]; then
do_something
fi
# Good pipeline
command1 \
| command2 \
| command3
Naming Conventions¶
- Variables: Use
lower_casefor local variables and loop variables. UseUPPER_CASEfor environment variables and global constants. - Functions: Use
snake_case(e.g.,my_function). - Files: Use
kebab-case(e.g.,my-script.sh). - Prefix: Prefix internal/private variables and functions with an underscore
_.
readonly MAX_RETRIES=3
some_local_var="value"
function process_data() {
local _internal_var="temp"
...
}
Variables and Parameters¶
- Quoting: Always quote variables (
"$var") unless you specifically want word splitting or glob expansion. - Local Variables: Always declare variables inside functions as
local. - Readonly: Use
readonlyfor constants. - Curly Braces: Use
${var}when necessary for clarity or avoiding ambiguity (e.g.,${var}_suffix), but"$var"is generally sufficient. - Unset Variables: Treat unset variables as an error (see Error Handling).
- Magic Variables: Avoid using
$1,$2directly in long functions. Assign them to meaningful variable names at the start of the function.
# Good
function greet() {
local name="$1"
echo "Hello, $name"
}
# Bad
function greet() {
echo "Hello, $1"
}
Input and Output¶
- Redirects: explicitly redirect input/output.
- Stderr: Print error messages to
stderr. - Printf: Prefer
printfoverechofor portability and reliability, especially when printing variable content that might start with-.
Control Flow¶
- Test Operator: Prefer
[[ ... ]]over[ ... ].[[is safer and offers more features (regex, no word splitting). - Arithmetic: Use
(( ... ))for arithmetic operations. - Loops: Use
for i in "${array[@]}"to iterate over arrays. - Case: Align
)with the pattern. End each case with;;.
Functions¶
- Definition: Use the
function name() { ... }orname() { ... }syntax. Be consistent.name() { ... }is more POSIX compliant, butfunctionkeyword makes it clear. - Return Values: Functions return exit codes (0-255). Use
echoorprintfto return string data (captured via command substitution). - Scope: Variables should be
localby default.
Command Substitution¶
- Syntax: Use
$(command)instead of backticks`command`. It is nestable and more readable. - Exit Codes: Check the exit code of command substitutions if critical.
Error Handling and Robustness¶
- Unofficial Strict Mode: Start scripts with the following (often called the "Bash strict mode"):
-e: Exit immediately if a command exits with a non-zero status.-u: Treat unset variables as an error.-o pipefail: The return value of a pipeline is the status of the last command to exit with a non-zero status.
- IFS: Be careful with
IFS(Internal Field Separator). If changing it, save the old value and restore it, or do it in a subshell. - Traps: Use
trapto clean up resources on exit.
Comments and Documentation¶
- Docstrings: While not standard, adding a header comment to functions explaining arguments and behavior is recommended.
- Inline: Explain why, not what.
- TODO: Use
# TODO(user): description.
Security¶
- Eval: Avoid
eval. It is dangerous and usually unnecessary. - Paths: Use absolute paths or explicitly relative paths (
./script) when invoking other scripts. - Sudo: Avoid
sudoinside scripts if possible. Let the user run the script withsudoif needed, or check for root privileges at the start. - Temporary Files: Use
mktempto create temporary files securely.
Clean Up and Signals¶
- Trap: Use
trapto catch signals (INT,TERM) andEXITto perform cleanup (deleting temp files, killing background processes).
Recommended Tools¶
- Linter:
shellcheck. Essential for finding bugs and bad practices. - Formatter:
shfmt. Enforces consistent formatting (indentation, etc.). - Testing:
bats-core(Bash Automated Testing System) for unit testing bash scripts.