← Linux Mastery: From Zero to Hero

Bash Scripting Fundamentals

Task 1
Making a script executable

Making a Script Executable

You've written a brilliant shell script and added the perfect shebang. You try to run it with ./my_script.sh and get this frustrating error:

bash: ./my_script.sh: Permission denied

Why? Because, by default, a new file is created with read and write permissions for the owner, but not execute permission. The system protects you from accidentally running a text file as a program.

To turn your text file into a command you can run, you need to give it the execute permission.

The Tool: chmod (Change Mode)

We use the chmod command, which we learned about in the File Permissions module, to add the execute bit.

Symbolic notation:

  • u – user (owner)
  • g – group
  • o – others
  • a – all (user + group + others)
  • + – add permission
  • x – execute permission

How to Make a Script Executable

Step 1: Check the Current Permissions

Use ls -l to view permissions.

ls -l my_script.sh
-rw-r--r-- 1 user user 123 Jan 16 11:30 my_script.sh

The first string -rw-r--r-- shows the permissions. Since there is no x, the script cannot be executed.

Step 2: Add Execute Permission

Option 1: Make executable for the owner only

chmod u+x my_script.sh

u+x means add execute permission for the user/owner.

Option 2: Make executable for everyone

chmod a+x my_script.sh

or the common shortcut:

chmod +x my_script.sh

a+x adds execute permission for all users.

Step 3: Verify the Change

Check the permissions again.

ls -l my_script.sh

-rwxr--r-- 1 user user 123 Jan 16 11:30 my_script.sh

or if executable for everyone:

-rwxr-xr-x 1 user user 123 Jan 16 11:30 my_script.sh

The presence of x means the file can now be executed.

Step 4: Run the Script

# Run script from the current directory
./my_script.sh

If the script is inside a directory listed in your $PATH (such as ~/bin), you can run it directly:

my_script.sh
Task 2
Variables and Command Substitution

Why Use Variables?

Variables are like labeled boxes where you can store information (text, numbers, filenames) for your script to use later.

They are essential for:

  • Avoiding repetition: Store a value once and reuse it many times.
  • Making scripts configurable: Use variables for values that may change.
  • Capturing output: Store the result of a command to reuse in your script.

Using Variables in Bash

1. Setting a Variable (The Box Label)

To create a variable, choose a name and assign a value using the = operator. No spaces are allowed around the equals sign.

# Store text
name="Alice"
filename="report.txt"

# Store a number
count=42

# Store command output
current_date=$(date)

2. Using a Variable (Accessing the Value)

To access the value inside a variable, prefix the variable name with a $.

# Print the variable value
echo "Hello, $name"

# Use the variable inside a command
cp $filename /backup/$filename

You can also use curly braces {} to clearly separate variable names from surrounding text. This is called parameter expansion.

# Without braces (incorrect)
echo "The file is $filename_backup"

# With braces (correct)
echo "The file is ${filename}_backup"

3. Important Rules

  • Variable names are case-sensitive. $name and $NAME are different.
  • By convention: uppercase for constants, lowercase for script variables.
  • No spaces around the equals sign. count=42 is correct, but count = 42 causes an error.

Command Substitution: $(command)

Command substitution allows you to capture the output of a command and store it inside a variable.

Syntax:

variable_name=$(command_to_run)

Examples

# Store the current date and time
current_time=$(date)
echo "The script started at $current_time"

# Store the system hostname
server_name=$(hostname)
echo "Running on server: $server_name"

# Count number of lines in a file
line_count=$(wc -l < /etc/passwd)
echo "There are $line_count users on the system."

# Store a configuration file path
config_file=$(find /etc -name "nginx.conf" 2>/dev/null | head -1)
echo "Found config at: $config_file"
Task 3
Control Structures

Control Structures in Bash

What are Control Structures?

So far, our scripts have run commands in a straight line. Control structures change this flow. They allow scripts to make decisions, repeat tasks, and choose different paths based on conditions.

This transforms a simple list of commands into a powerful program.

The two main types of control structures are:

  • Conditionals: Execute code based on conditions (if, case).
  • Loops: Repeat commands multiple times (for, while, until).

1. Conditional Execution: The if Statement

The if statement allows a script to make decisions. Basic logic: "If this condition is true, execute the commands."

Basic Syntax

if [ condition ]; then
  # commands to run if condition is TRUE
fi

if-else Syntax

if [ condition ]; then
  # commands if TRUE
else
  # commands if FALSE
fi

if-elif-else Syntax

if [ condition1 ]; then
  # commands if condition1 is TRUE
elif [ condition2 ]; then
  # commands if condition2 is TRUE
else
  # commands if all conditions are FALSE
fi

Example: Safe File Deletion

#!/bin/bash
filename="$1"

if [ -f "$filename" ]; then
  echo "File found. Deleting $filename..."
  rm "$filename"
else
  echo "Error: File '$filename' does not exist."
fi

2. The case Statement

The case statement is useful when comparing a variable against multiple patterns. It works like a multi-choice if statement.

Syntax

case $variable in
  pattern1)
    # commands
    ;;
  pattern2)
    # commands
    ;;
  *)
    # default commands
    ;;
esac

Example: Simple Menu System

#!/bin/bash
echo "Select an option: start|stop|status"
read choice

case $choice in
  start)
    echo "Starting the service..."
    systemctl start my-service
    ;;
  stop)
    echo "Stopping the service..."
    systemctl stop my-service
    ;;
  status)
    echo "Checking status..."
    systemctl status my-service
    ;;
  *)
    echo "Error: Invalid option. Use start, stop, or status."
    exit 1
    ;;
esac

3. Loops: Automating Repetition

A. The for Loop

The for loop is used to iterate through a list of items.

Syntax

for item in list_of_items; do
  # commands
done

Example

#!/bin/bash
# Script to greet friends

for name in Alice Bob Charlie; do
  echo "Hello, $name! How are you today?"
done

Output

Hello, Alice! How are you today?
Hello, Bob! How are you today?
Hello, Charlie! How are you today?

B. The while Loop

The while loop runs commands repeatedly while a condition is true.

Syntax

while [ condition ]; do
  # commands
done

Example: Countdown Timer

#!/bin/bash

count=5

while [ $count -gt 0 ]; do
  echo "T-minus $count..."
  sleep 1
  count=$((count - 1))
done

Output

T-minus 5...
T-minus 4...
T-minus 3...
T-minus 2...
T-minus 1...

C. The until Loop

The until loop runs commands until a condition becomes true. It works opposite to the while loop.

Syntax

until [ condition ]; do
  # commands
done

Example: Wait for a Website to Respond

#!/bin/bash

echo "Waiting for example.com to come online..."

until ping -c 1 example.com &> /dev/null; do
  echo "Site is unreachable. Retrying in 3 seconds..."
  sleep 3
done

echo "SUCCESS: example.com is now reachable!"

Output

Waiting for example.com to come online...
Site is unreachable. Retrying in 3 seconds...
Site is unreachable. Retrying in 3 seconds...
SUCCESS: example.com is now reachable!