Style
This section outlines style guidelines that directly impact the functionality of Bash scripts. It includes best practices for quoting and variable declaration.
Using Quotes
In Bash, the type of quotes you use—single, double, or none—affects how strings and variables are interpreted. Understanding when to use each type is crucial to avoid common pitfalls, such as unintended word splitting, globbing, and parameter expansion.
Guidelines
- First Choice: Use double quotes as the default method of quoting strings and variables.
- Reason: Double quotes allow the shell to interpret expansions (such as variables, command substitutions, and escape sequences) while preventing word splitting and globbing.
- String Literals: Use single quotes to define string literals.
- Reason: Single quotes preserve the literal value of every character in a string, preventing the shell from performing expansions or substitutions. This behavior is essential for commands like
find
,grep
, andawk
, which have their own rules for interpreting special characters. Using single quotes ensures that these commands receive the input exactly as written, without modification by the shell.
- Reason: Single quotes preserve the literal value of every character in a string, preventing the shell from performing expansions or substitutions. This behavior is essential for commands like
- When to Omit Quotes: In certain cases, quotes can be safely omitted without risk. Common scenarios include arithmetic operations and double bracket tests.
- Arithmetic Operations (
$(( ... ))
): Arithmetic expansion treats the content as a single unit, preventing word splitting and globbing. - Double Bracket Tests
[[ ... ]]
: The[[ ... ]]
syntax is a Bash built-in that protects against word splitting and globbing, allowing comparisons and checks without the need for quotes.
- Arithmetic Operations (
- Syntax Highlighting: Enable syntax highlighting in your IDE or text editor.
- Reason: Syntax highlighting visually distinguishes different code elements, such as keywords, strings, and variables, making it easier to identify potential issues, such as missing quotes, misplaced escape sequences, or incorrect special characters. This feature enhances debugging by making errors more visible and improving overall code readability.
- When in Doubt, Quote: If unsure whether quotes are necessary, it's recommended to use them. Quoting variables and strings by default helps prevent common pitfalls.
Declaring and Naming Variables
Bash offers several methods for declaring variables, each with implications for scope, immutability, and readability. Following best practices for naming and declaring variables is essential to maintain consistency and avoid unintended side effects
Guidelines
- Naming Convention: Use
snake_case
for variable names. -
Scope Management: Use the
local
keyword to limit a variable's scope to within a function.-
Reason: Declaring a variable as
local
prevents accidental overwrites of global variables with the same name. It also ensures that once the function completes, the variable is unset and released from memory. (1)- Variable Scope: In Bash, variables declared inside a function without the
local
keyword are global by default, meaning they persist beyond the function's scope and can impact other parts of the script.
- Variable Scope: In Bash, variables declared inside a function without the
-
Example:
Explanation: In this example,my_var="global value" my_function() { # This local variable won't overwrite the global variable. local my_var="local value" echo "$my_var" } my_function echo "$my_var"
my_var
is defined both globally and locally withinmy_function
. Thelocal
keyword ensures that themy_var
inside the function is distinct from the globalmy_var
. When the function is called, it outputs the local value, whereas the finalecho
statement outputs the global value.
-
-
Naming Conventions: Use
UPPER_SNAKE_CASE
with aC_
prefix for constants (e.g.,C_CONFIG_FILE_PATH
).-
Reason: This naming convention helps distinguish constants from other variables and aligns with common practices across various programming languages. (1)
- Constants and Environment Variables: Constants typically use
UPPER_SNAKE_CASE
, a standard followed in many languages, such as C, Python, and Java. Adopting a common convention ensures that other developers can easily recognize constants in your scripts. However, in Unix-like systems, environment variables are also written inUPPER_SNAKE_CASE
. To prevent confusion, a prefix such asC_
is used to differentiate constants from environment variables.
- Constants and Environment Variables: Constants typically use
-
Alternative Prefix: While
C_
is the default prefix for constants, other prefixes likeCONST_
orCONFIG_
may be used if they better suit your naming conventions or project requirements. The key is to maintain consistency within your script or project.- Example: The oh-my-zsh project uses the
OMZ_
prefix to clearly indicate constants as part of the project while following theUPPER_SNAKE_CASE
convention.
- Example: The oh-my-zsh project uses the
-
-
Selective Use of
readonly
: Usereadonly
to define constants that must remain unchanged throughout the script.- Reason: The
readonly
keyword enforces immutability, preventing accidental modification of critical values. However, overusingreadonly
can make scripts overly restrictive and complicate development and debugging. Therefore, reservereadonly
for values that are truly immutable. - Declaration: Use
readonly
when initializing the variable or immediately afterward. - Example of When to Use:
- Global Constants: Apply
readonly
to global constants where changes could significantly affect script behavior or cause unexpected results.
- Global Constants: Apply
- Example of When to Omit:
- Function-Dependent Initialization: Avoid using
readonly
when a constant's value is determined by a function or a complex operation.- Reason: Applying
readonly
to a variable initialized by a function can cause errors if the function is called multiple times. To avoid reinitialization errors, omitreadonly
unless safeguards are in place to prevent reassignment.
- Reason: Applying
- Function-Dependent Initialization: Avoid using
- Reason: The
Example
#!/bin/bash
## Constant variables using the naming convention with 'C_' prefix.
readonly C_CONFIG_FILE_PATH="/etc/myapp/config.conf"
readonly C_MAX_RETRIES=5
readonly C_API_ENDPOINT="https://api.example.com/data"
## Function that determines a value based on conditions.
initialize_dynamic_constant() {
local environment="$1"
if [[ "$environment" == "production" ]]; then
C_DYNAMIC_URL="https://prod.example.com/api"
else
C_DYNAMIC_URL="https://dev.example.com/api"
fi
}
# Call the function to initialize the dynamic constant.
initialize_dynamic_constant "production"
# Use the initialized values (note that C_DYNAMIC_URL is not readonly).
echo "API Endpoint: $C_API_ENDPOINT"
echo "Max Retries: $C_MAX_RETRIES"
echo "Configuration File Path: $C_CONFIG_FILE_PATH"
echo "Dynamic URL: $C_DYNAMIC_URL"
# Attempting to modify a readonly variable will result in an error.
# Uncommenting the line below will cause the script to fail.
#C_API_ENDPOINT="https://newapi.example.com"
## Function to perform an operation based on the constants.
perform_operation() {
for ((i = 1; i <= C_MAX_RETRIES; i++)); do
echo "Attempt $i: Fetching data from $C_API_ENDPOINT"
sleep 1 # Simulate API call.
break # Break after first attempt for demo purposes.
done
}
# Perform the operation using the constants.
perform_operation
-
Naming Conventions: Use
UPPER_SNAKE_CASE
with anE_
prefix for exported variables (e.g.,E_PATH
).-
Reason: This naming convention differentiates exported variables from other variable types and aligns with the Unix naming convention for environment variables. (1)
- Exported and Environment Variables: Under Unix conventions, environment variables are written in
UPPER_SNAKE_CASE
. However, just like constants, using the same naming convention for exported variables can cause confusion and lead to accidental modifications of existing environment variables. A prefix such asE_
is used to differentiate exported variables from others while adhering to theUPPER_SNAKE_CASE
convention.
- Exported and Environment Variables: Under Unix conventions, environment variables are written in
-
-
Declaration: Use
export
when initializing an exported variable or immediately afterward.
- Selective Use: Use the
declare
command to manage advanced variable attributes, such as associative arrays. For simple variable declarations, or when keywords likelocal
andreadonly
are sufficient, avoid usingdeclare
.- Reason: The
declare
command is useful for managing complex variable types, such as associative arrays, or when specific attributes are needed. However, usingdeclare
for basic variable assignments can reduce readability and is often unnecessary.
- Reason: The
Example
# Associative array containing the configuration settings for sshd_config.
declare -A C_SSHD_CONFIG=(
["LogLevel"]="VERBOSE"
["LoginGraceTime"]="30"
["PermitRootLogin"]="no"
["MaxAuthTries"]="3"
["MaxSessions"]="2"
["PubkeyAuthentication"]="yes"
["PermitEmptyPasswords"]="no"
["ChallengeResponseAuthentication"]="no"
["KbdInteractiveAuthentication"]="no"
["UsePAM"]="yes"
["AllowAgentForwarding"]="no"
["AllowTcpForwarding"]="no"
["X11Forwarding"]="no"
["PrintMotd"]="no"
["TCPKeepAlive"]="no"
["Compression"]="no"
["ClientAliveInterval"]="300"
["ClientAliveCountMax"]="2"
)
readonly C_SSHD_CONFIG