Solid base for Bash scripting
Bash scripting is a powerful tool for automating tasks, managing your system operations on Unix-like environment. This guide covers essential Bash concepts.
Shebang
First thing in your script the shebang #!
is used at the beginning of a script to specify the interpreter that should execute the script. You should prefer #!/usr/bin/env bash
to ensure portability on different systems, as it locates the bash
interpreter using the env
command. #!/bin/bash
can cause issues and do launch your script.
#!/usr/bin/env bash
#!/usr/bin/env python3
Command Line Arguments
Command line arguments allow you to pass data to your script when executing it.
Example:
#!/usr/bin/env bash
first_name=$1
last_name=$2
echo "Hello, my name is $first_name $last_name."
Explanation:
$1
and$2
represent the first and second command line arguments, respectively.- You can run the script as
./script.sh John Doe
to output:Hello, my name is John Doe.
Handling Quotes and Variables:
-
Single Quotes (
'
): Preserve the literal value of each character within the quotes. Variables are not expanded.echo 'Hello, $name' # Outputs: Hello, $name
-
Double Quotes (
"
): Allow variable expansion and interpretation of certain escape sequences.echo "Hello, $name" # Outputs: Hello, John
Using ${}
for Variable Manipulation:
The ${}
syntax allows for more complex variable operations within strings.
name=""
echo "Hello, ${name:-Anonymous}" # Outputs: Hello, Anonymous
# Setting the variable if it's unset or empty
echo "Hello, ${name:="Anonymous"}" # Sets name to "Anonymous" and outputs: Hello, Anonymous
Explanation:
${variable:-default}
: Usesdefault
ifvariable
is unset or empty.${variable:=default}
: Setsvariable
todefault
if it’s unset or empty and then uses it.
Subshells
A subshell allows you to execute commands in a separate shell instance. Changes made in a subshell do not affect the parent shell.
Example:
#!/usr/bin/env bash
echo "Current Directory: $(pwd)"
(cd ..; echo "Subshell Directory: $(pwd)")
echo "Back to Directory: $(pwd)"
Output:
Current Directory: /home/user/dev
Subshell Directory: /home/user
Back to Directory: /home/user/dev
Explanation:
(cd ..; pwd)
: Changes directory to the parent in a subshell and prints it.- The parent shell’s directory remains unchanged.
Command Substitution
Command substitution allows the output of a command to replace the command itself within a shell command.
Syntax:
variable=$(command)
Example:
#!/usr/bin/env bash
current_dir=$(pwd)
echo "You are in: $current_dir"
Output:
You are in: /home/user
Explanation:
$(pwd)
: Executes thepwd
command and assigns its output tocurrent_dir
.
Process Substitution
Process substitution allows you to use the output of a process as if it were a file. This is particularly useful for commands that require file inputs.
Example:
#!/usr/bin/env bash
diff <(ls ./folder1) <(ls ./folder2)
Explanation:
<(ls ./folder1)
and<(ls ./folder2)
create temporary file descriptors for the outputs of thels
commands.diff
compares the contents of these directories.
Arithmetic Operations
Bash supports arithmetic operations using the $(())
syntax.
Example:
#!/usr/bin/env bash
a=5
b=3
sum=$((a + b))
echo "Sum: $sum" # Outputs: Sum: 8
Supported Operations:
- Addition (
+
) - Subtraction (
-
) - Multiplication (
*
) - Division (
/
) - Modulus (
%
) - Exponentiation (
**
)
Conditional Statements
Conditional statements allow your script to make decisions based on certain conditions.
Basic if
Statement:
#!/usr/bin/env bash
if [ "$1" -gt 0 ]; then
echo "The number is positive."
else
echo "The number is not positive."
fi
Explanation:
[ "$1" -gt 0 ]
: Checks if the first argument is greater than 0.- Executes the corresponding block based on the condition.
Comparison Types:
- String Comparison:
=
,!=
,<
,>
- Numeric Comparison:
-eq
,-ne
,-lt
,-le
,-gt
,-ge
- File Tests:
-e
(exists),-f
(file),-d
(directory),-r
(readable),-w
(writable),-x
(executable)
Exit Codes
Scripts and commands return an exit code to indicate success or failure.
0
: Successn
(wheren
is a non-zero value): Failure or specific error codes
Example:
#!/usr/bin/env bash
if [ -f "$1" ]; then
echo "File exists."
exit 0
else
echo "File does not exist."
exit 1
fi
Usage:
- Check the exit code using
echo $?
after running a script or command.
Conditional Execution
Bash allows commands to be executed based on the success or failure of previous commands using &&
and ||
.
Example:
#!/usr/bin/env bash
mkdir new_directory || { echo "Failed to create directory."; exit 1; }
echo "Directory created successfully."
Explanation:
mkdir new_directory || { ...; }
: Ifmkdir
fails, execute the commands within{}
.
Another Example:
false || true
echo "Continue executing the script..."
Output:
Continue executing the script...
Explanation:
false
returns a non-zero exit code, triggering the execution oftrue
.- The script continues despite the
false
command failing.
Useful Commands
Sleep
The sleep
command pauses the execution of a script for a specified duration.
#!/usr/bin/env bash
echo "Waiting for 5 seconds..."
sleep 5
echo "Done waiting."
Usage:
sleep 5
: Pauses for 5 seconds.sleep 1m
: Pauses for 1 minute.
Read
The read
command takes input from the user during script execution.
#!/usr/bin/env bash
read -p "Enter your name: " name
echo "Hello, $name!"
Explanation:
-p
: Displays a prompt message.read
assigns the user input to the variablename
.
Strict Mode
Enabling strict mode makes your script more robust by enforcing better error handling and variable usage.
#!/usr/bin/env bash
set -euo pipefail
Explanation:
set -e
: Exit immediately if a command exits with a non-zero status.set -u
: Treat unset variables as an error and exit immediately.set -o pipefail
: Causes a pipeline to return the exit status of the last command in the pipe that failed.
Loops
Loops allow you to execute a block of code multiple times.
For Loop
Example: Iterating Over an Array
#!/usr/bin/env bash
array=(1 2 3 4)
for item in "${array[@]}"; do
echo "Item: $item"
done
Output:
Item: 1
Item: 2
Item: 3
Item: 4
Explanation:
${array[@]}
: Expands to all elements in the array.- The loop iterates over each element, assigning it to
item
.
Example: Pattern Matching
#!/usr/bin/env bash
for file in ./content/*.md; do
cat "$file"
done
Explanation:
- Iterates over all Markdown files in the
./content
directory and displays their contents.
While Loop
Example: Waiting for a Kubernetes Pod to be Running
#!/usr/bin/env bash
POD_NAME="my-pod"
NAMESPACE="default"
echo "Waiting for pod $POD_NAME to be Running..."
while true; do
status=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.phase}')
if [ "$status" = "Running" ]; then
echo "Pod is running."
break
fi
echo "Current status: $status. Waiting..."
sleep 5
done
Explanation:
- Continuously checks the status of a Kubernetes pod every 5 seconds until it is
Running
.
Script Structuring
Splitting Scripts
Breaking down your script into multiple files can improve readability and maintainability.
Executing Another Script:
-
Same Shell:
#!/usr/bin/env bash # main.sh . ./networkSetup.sh arg1 arg2
Explanation:
- The
.
(dot) command sourcesnetworkSetup.sh
in the current shell, allowing it to share variables and functions.
- The
-
Subshell:
#!/usr/bin/env bash # main.sh ./networkSetup.sh arg1 arg2
Explanation:
- Executes
networkSetup.sh
in a subshell, isolating its environment from the main script.
- Executes
Functions
Functions allow you to reuse code blocks within your script.
Example:
#!/usr/bin/env bash
greet() {
local name="$1"
echo "Hello, $name!"
}
greet "Alice"
greet "Bob"
Output:
Hello, Alice!
Hello, Bob!
Explanation:
greet
is a function that takes one argument and prints a greeting.local
: Limits the scope of thename
variable to within the function.
Temporary Files
Creating temporary files and directories is essential for handling intermediate data securely.
Example:
#!/usr/bin/env bash
# Create a temporary file
tempfile=$(mktemp)
trap "rm -f $tempfile" EXIT
echo "Hello, YouTube!" > "$tempfile"
echo "Content written to $tempfile."
# Create a temporary directory
tempdir=$(mktemp -d)
trap "rm -rf $tempdir" EXIT
echo "Hello, YouTube (from inside a tempdir)!" > "$tempdir/hello"
echo "Content written to $tempdir/hello."
Explanation:
mktemp
: Creates a unique temporary file or directory.trap
: Ensures that the temporary file and directory are deleted when the script exits, even if interrupted.
Naming Conventions
Adopting consistent naming conventions enhances the readability and organization of your scripts.
Examples:
-
Script Files:
- Use descriptive names like
backup.sh
,deploy_app.sh
, orlaunch.sh
.
- Use descriptive names like
-
Conventions:
- Prefix scripts that start processes with
launch
, e.g.,launch_server.sh
. - Place scripts in appropriate directories, such as
bin/
for executable scripts.
- Prefix scripts that start processes with
PATH Environment Variable
The PATH
environment variable specifies directories where the shell looks for executable files.
Viewing PATH:
echo $PATH
Example Output:
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
Adding a Directory to PATH:
To make your scripts executable from anywhere, add their directory to PATH
by editing your shell configuration file (~/.bashrc
or ~/.zshrc
).
export PATH="$PATH:/path/to/your/scripts"
Steps:
-
Open
~/.bashrc
or~/.zshrc
in a text editor. -
Add the export line above, replacing
/path/to/your/scripts
with your script directory. -
Reload the configuration:
source ~/.bashrc # or source ~/.zshrc
Process of Script Creation
Creating an effective Bash script involves several steps to ensure it meets your needs and handles various scenarios gracefully.
-
Figure Out What You Want to Do:
- Define the goal of your script.
- Break down the task into smaller steps.
-
Identify Tools You’ll Use:
- Determine which Unix commands and utilities are needed.
-
Sketch It Out in the Terminal:
- Experiment with commands directly in the terminal to understand their behavior.
-
Copy It into a Script:
- Create a new
.sh
file and add your commands.
- Create a new
-
Pull Out Variables and Inputs:
- Replace hard-coded values with variables and command line arguments.
-
Add Checks (Guards, etc.):
- Implement error handling and input validation.
-
Add Loops and Other Advanced Functionality:
- Incorporate loops, conditionals, and functions as needed.
-
Break into Multiple Files if Necessary:
- If the script becomes too large, split it into modular scripts and source them.
Cheatsheet
For quick reference, check out the Bash Scripting Cheatsheet.