Zsh 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 Zsh scripting within this project. While Zsh shares many similarities with Bash, it offers distinct features and default behaviors that require specific attention.
Table of Contents¶
- General Principles
- File Header and Extension
- Formatting and Indentation
- Naming Conventions
- Variables and Parameters
- Arrays and Associative Arrays
- Input and Output
- Control Flow
- Functions and Autoloading
- Globbing and Expansion
- Error Handling and Robustness
- Comments and Documentation
- Security
- Recommended Tools
General Principles¶
- Leverage Zsh Features: Use Zsh's powerful features (glob qualifiers, recursive globbing, associative arrays) where they improve clarity or performance over standard POSIX sh constructs.
- Robustness: Scripts should fail early and loudly.
- Readability: Code should be self-documenting. Use descriptive names.
- Consistency: Follow the style of the existing codebase.
File Header and Extension¶
- Shebang: Always start with a shebang.
- Extension: Executable scripts should generally not have an extension. Library files or sourced scripts should end in
.zsh. - 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, extending to 100 if it improves readability.
- Pipelines: Break long pipelines across lines, ending with
|. - Blocks:
then,doshould be on the same line as the condition/loop command.
# Good
if [[ -f "$file" ]]; then
print "File exists"
fi
# Good pipeline
ls -1 \
| grep "pattern" \
| sort
Naming Conventions¶
- Variables:
lower_casefor local/loop variables.UPPER_CASEfor environment/global variables. - Functions:
snake_case(e.g.,parse_input). - Files:
kebab-case(e.g.,install-deps.zsh). - Private Members: Prefix internal functions/variables with
_(e.g.,_helper_func).
Variables and Parameters¶
- Declaration: Use
localortypesetinside functions to prevent polluting the global scope.- Note: In Zsh,
typesetis equivalent tolocalinside functions.
- Note: In Zsh,
- Quoting: Unlike Bash, Zsh does not split words on unquoted variable expansion by default. However, always quote strings (
"$var") that might contain empty values or if portability habits are desired.- Exception: Intentionally unquoted variables for splitting (though explicit splitting flags
${=var}are preferred).
- Exception: Intentionally unquoted variables for splitting (though explicit splitting flags
- Types: Use
integerfor math variables andfloatfor floating point to enforce typing.
Arrays and Associative Arrays¶
- Indexing: Zsh arrays are 1-based by default. Do not assume 0-based indexing unless
setopt KSH_ARRAYSis widely used (discouraged in pure Zsh scripts). - Declaration:
- Iteration:
Input and Output¶
- Print: Prefer
printorprintfoverecho.printis a Zsh builtin with consistent behavior.print -rprints raw strings (ignores escapes).print -lprints elements separated by newlines.
- Stderr:
Control Flow¶
- Conditions: Use
[[ ... ]]exclusively. It handles regex (=~) and globbing safely. - Arithmetic: Use
(( ... ))for math comparisons and operations. - Loops:
for x in ...; do ...; donewhile ...; do ...; done- Short form
forloops (e.g.,for x (...)) are valid but standard syntax is often more readable for complex blocks.
Functions and Autoloading¶
- Definition: Prefer
function_name() { ... }. Thefunctionkeyword is optional but can be used. - Autoloading: For large suites of functions, use
autoload -Uz function_nameand keep functions in separate files in$fpath. This improves startup time. - Return: Return status codes (0-255). Output data via stdout.
Globbing and Expansion¶
- Recursive Globbing: Use
**freely for recursive searches. - Qualifiers: Use glob qualifiers to filter files efficiently without piping to
find.*(/): directories only*(.): plain files only*(x): executables*(m-1): modified in the last day
- Modifiers: Use modifiers like
:t(tail/filename),:h(head/dirname),:r(remove extension).
Error Handling and Robustness¶
- Options:
setopt ERR_EXIT(orset -e): Exit on error.setopt NO_UNSET(orset -u): Error on unset variables.setopt PIPE_FAIL(orset -o pipefail): Check all commands in a pipe.
- Standard Preamble:
- Traps: Use
trapfor cleanup.
Comments and Documentation¶
- Header: Scripts should explain their purpose at the top.
- Functions: Document inputs, outputs, and side effects.
- Inline: Explain why tricky logic is used.
Security¶
- Eval: Avoid
eval. - Temporary Files: Use
mktempor Zsh's=(command)substitution if appropriate for temporary file handling. - Paths: Use absolute paths for critical commands or ensure
$PATHis trusted.
Recommended Tools¶
- Syntax Check:
zsh -n script.zshchecks for syntax errors without running. - Debugging:
zsh -x script.zshtraces execution.