Bash Programming

This guide covers the core concepts of Bash scripting, ranging from basic syntax to control structures, functions, and error handling.


1. Introduction to Bash

Bash (Bourne Again SHell) is a command-line interpreter and scripting language widely used in Unix-like operating systems. A Bash script is a plain text file containing a sequence of commands.

Creating and Running Your First Script

1. Create a file named myscript.sh.
2. Add the Shebang: The very first line of a Bash script must specify the path to the interpreter.

   #!/bin/bash
   echo "Hello, World!"

3. Make the script executable: By default, new files do not have execution permissions. Use chmod to grant permissions:

   chmod +x myscript.sh

4. Execute the script:

   ./myscript.sh

2. Basic Syntax and Variables

Comments

Comments start with a # symbol and are ignored by the shell.

# This is a single-line comment
echo "This will run" # This is an inline comment

Variables

Variables in Bash do not require type declaration.

  • Rules:
  • No spaces around the = sign during assignment.
  • Variable names are case-sensitive and typically written in uppercase by convention, though lowercase is also acceptable.
  • To access a variable, prefix its name with $.
# Correct assignment
name="Alice"
age=30

# Accessing variables
echo "My name is $name and I am $age years old."

Quoting Rules

How you quote variables determines how Bash processes them:

  • Double Quotes ("): Allows variable expansion and escape characters.
  • Single Quotes ('): Treats all characters literally. No expansion occurs.
  • Backticks (` `) or $()`: Used for command substitution (running a command and saving its output).
greeting="Hello"

echo "$greeting World"   # Outputs: Hello World
echo '$greeting World'   # Outputs: $greeting World

# Command substitution
current_dir=$(pwd)
echo "You are currently in: $current_dir"

3. Special Variables and Positional Parameters

Bash uses special variables to handle arguments passed to a script from the command line.

VariableDescription
$0The name of the script itself
$1 to $9The first nine arguments passed to the script
${10}Tenth and subsequent arguments (require curly braces)
$#The number of arguments passed to the script
$@All arguments passed to the script (individually quoted)
$*All arguments passed to the script (joined as a single string)
$?The exit status of the last executed command (0 means success)
$$The process ID (PID) of the current shell

Example Script (arguments.sh):

#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Total arguments: $#"
echo "All arguments: $@"

Execution:

./arguments.sh Apple Banana

Output:

Script name: ./arguments.sh
First argument: Apple
Second argument: Banana
Total arguments: 2
All arguments: Apple Banana

4. Input and Output

Reading User Input

The read command pauses execution to accept input from the standard input.

#!/bin/bash
# -p flag allows you to specify a prompt string
read -p "Enter your username: " username
echo "Welcome, $username!"

Prompting for Sensitive Information

Use the -s flag to hide the input (useful for passwords).

read -s -p "Enter password: " password
echo -e "\nPassword saved securely."

5. Conditional Statements

Bash uses if statements to make decisions. The test conditions are enclosed in brackets. It is recommended to use double brackets [[ ... ]] in modern Bash, as they are safer and offer more features than single brackets [ ... ].

Basic If-Else Syntax

#!/bin/bash
read -p "Enter a number: " num

if [[ $num -gt 10 ]]; then
    echo "The number is greater than 10."
elif [[ $num -eq 10 ]]; then
    echo "The number is exactly 10."
else
    echo "The number is less than 10."
fi

Comparison Operators

Integer Comparisons:

  • -eq : Equal to
  • -ne : Not equal to
  • -lt : Less than
  • -le : Less than or equal to
  • -gt : Greater than
  • -ge : Greater than or equal to

String Comparisons:

  • == : Equal to
  • != : Not equal to
  • -z : String is empty
  • -n : String is not empty

File Checks:

  • -e file : File or directory exists
  • -f file : File exists and is a regular file
  • -d file : Directory exists
  • -r file : File has read permission
  • -w file : File has write permission
  • -x file : File has execute permission

Example File Check:

file_path="config.txt"

if [[ -f "$file_path" ]]; then
    echo "$file_path exists and is a regular file."
else
    echo "$file_path does not exist."
fi

6. Loops (For, While, Until)

For Loops

For loops iterate over a sequence or a list of items.

List Iteration:

for fruit in apple banana cherry; do
    echo "Fruit: $fruit"
done

Range Iteration:

# Prints 1 to 5
for i in {1..5}; do
    echo "Number: $i"
done

C-Style For Loop:

for ((i=0; i<3; i++)); do
    echo "Counter: $i"
done

While Loops

A while loop runs as long as the specified condition evaluates to true.

counter=1
while [[ $counter -le 3 ]]; do
    echo "Count is $counter"
    ((counter++))
done

Until Loops

An until loop runs as long as the specified condition evaluates to false (it stops once the condition becomes true).

counter=1
until [[ $counter -gt 3 ]]; do
    echo "Count is $counter"
    ((counter++))
done

7. Functions

Functions allow you to group blocks of code for reuse.

Syntax and Scope

#!/bin/bash

# Function definition
greet_user() {
    # 'local' makes the variable accessible only within this function
    local guest_name=$1
    echo "Hello, $guest_name!"
}

# Invoking the function and passing an argument
greet_user "Charlie"

Return Values

Functions in Bash do not return data values in the traditional sense. Instead, they return an exit status code (0 to 255) using the return keyword, or output text which can be captured.

Option 1: Using Return Status Codes (Numeric)

check_status() {
    return 1 # Signifies a specific error or state
}

check_status
status=$?
echo "The exit status was: $status"

Option 2: Returning String Output

get_greeting() {
    echo "Welcome back!"
}

# Capture the printed output of the function
message=$(get_greeting)
echo "Message: $message"

8. Arrays

Bash supports one-dimensional arrays.

Indexed Arrays

# Define an array
colors=("red" "green" "blue")

# Access elements (index starts at 0)
echo "First color: ${colors[0]}"

# Access all elements
echo "All colors: ${colors[@]}"

# Get the length of the array
echo "Array size: ${#colors[@]}"

# Append an element
colors+=("yellow")

9. Arithmetic Operations

By default, Bash treats variables as strings. To perform math, you need to use specific constructs.

Double Parentheses (( ... ))

This is the standard way to perform integer arithmetic in Bash.

num1=15
num2=5

sum=$((num1 + num2))
difference=$((num1 - num2))
product=$((num1 * num2))
quotient=$((num1 / num2)) # Integer division

echo "Sum: $sum"
echo "Quotient: $quotient"

Floating-Point Arithmetic

Bash does not natively support decimal calculations. For non-integers, pipe the expression to bc (Basic Calculator).

# Scale sets the number of decimal places
result=$(echo "scale=2; 10 / 3" | bc)
echo "Result: $result" # Outputs 3.33

10. Redirection and Pipelines

Standard Streams

  • stdin (Standard Input) - File descriptor 0
  • stdout (Standard Output) - File descriptor 1
  • stderr (Standard Error) - File descriptor 2

Redirection Operators

  • > : Overwrites output to a file.
  • >> : Appends output to a file.
  • 2> : Redirects error output.
  • &> : Redirects both standard output and standard error.
  • < : Reads input from a file.

Examples:

# Redirect output to a file (overwrites existing content)
echo "Line 1" > output.txt

# Append to a file
echo "Line 2" >> output.txt

# Redirect error messages to a file
ls non_existent_dir 2> error.log

# Discard both output and error output entirely
command_name &> /dev/null

Pipelines

Pipes (|) allow you to pass the stdout of one command as the stdin to another command.

# List files, filter for those containing "log", and count the lines
ls | grep "log" | wc -l

11. Debugging and Error Handling

Writing clean, robust Bash scripts requires proactive error checking.

Common Debugging Flags

You can enable debugging options within your script by placing them near the top.

# Enable these options at the start of your script
set -e # Terminate script immediately if any command returns a non-zero status.
set -u # Treat unset variables as an error and exit immediately.
set -o pipefail # Catch errors occurring within a pipeline of commands.

Or combining them:

#!/bin/bash
set -euo pipefail

Debugging Line-by-Line

To see exactly what commands Bash executes, use the -x option:

  • On execution: bash -x script.sh
  • Within the script:
  set -x # Turn on debugging output
  echo "This part is being debugged"
  set +x # Turn off debugging output

Standard Exit Codes

Always exit your script with an appropriate exit code.

if [[ -f "important_file.txt" ]]; then
    echo "File found."
    exit 0 # Success
else
    echo "Critical file missing." >&2
    exit 1 # Failure
fi

The guide was created in June 2026.