Shell Scripts for Beginners: Chapter 1 — Shell Script Introduction
Series: Shell Scripts for Beginners Chapter: Shell Script Introduction Topics Covered: Course Introduction · Project Introduction · Creating Your First Shell Script · Variables · Command Line Arguments · Read Inputs · Arithmetic Operations
Welcome — What This Blog Is About
If you've ever found yourself typing the same Linux commands over and over again — backing up files, checking system health, restarting services — you've already felt the pain that shell scripting solves. This blog is the written companion to Chapter 1 of the Shell Scripts for Beginners course. It walks you through everything from "what even is a shell script?" all the way to doing math inside your scripts.
No programming experience required. Seriously. If you can open a terminal and type ls, you're ready.
We'll be learning through a fun, real-world project: automating a Smart Home Control System using shell scripts. Think of it as building the brain of a smart home — turning devices on and off, checking their status, and running sequences of commands automatically. Let's dive in.
1. Course Introduction — Why Shell Scripting?
What Is a Shell Script?
A shell script is simply a text file containing a sequence of Linux commands that are executed one after another. Instead of typing ten commands manually every time, you write them once in a file, and the shell (the command-line interpreter) runs them all for you automatically.
Think of it like a recipe. A chef doesn't reinvent bread every morning — they follow a recipe. Shell scripts are your recipes for the terminal.
Why Should You Learn Shell Scripting?
Shell scripting is one of those foundational skills that pays dividends every single day for anyone working with computers, especially system administrators and DevOps engineers. Here's what it can help you do:
Automate repetitive tasks — daily backups, log rotation, system cleanup
Manage multiple servers — install packages or apply patches across dozens of machines simultaneously
Monitor your system — periodically check memory, CPU, and disk usage, and raise alerts when thresholds are crossed
Perform audits — identify logged-in users, track resource-hungry processes, review activity logs
Troubleshoot faster — search across multiple log files at once to pinpoint root causes
Who Is This For?
This course (and this blog) is designed for:
Absolute beginners with zero programming experience
System administrators and IT engineers looking to automate their workflows
Anyone curious about Linux automation
Prerequisite: Basic Linux command-line experience (knowing commands like ls, mkdir, cd) is all you need.
2. Project Introduction — The Smart Home Control System
To make learning concrete and fun, we'll build our scripts around a Smart Home Automation project.
The Scenario
You're the lead engineer for a Smart Home Control System. Your job is to automate the sequence of operations that power on, configure, and monitor various smart home devices — lights, thermostats, security cameras, and door locks.
Every morning, your home needs to:
Power on the central hub
Connect all devices to the home network
Run a system check on all devices
Activate the daily automation schedule
Confirm everything is running correctly
Doing this manually every day? Tedious. Letting a shell script do it? Elegant.
The Commands at Your Disposal
Just like Linux gives you commands such as ls, mkdir, and useradd, our Smart Home system comes with its own set of commands:
| Task | Command |
|---|---|
| Power on the hub | smarthome start hub |
| Connect devices to network | smarthome connect network |
| Run device health check | smarthome check devices |
| Activate daily schedule | smarthome activate schedule |
| Verify system status | smarthome status |
| List all devices | smarthome list |
| Add a new device | smarthome add <device-name> |
| Remove a device | smarthome delete <device-name> |
| Debug a failing device | smarthome debug <device-name> |
Don't worry about memorizing these — we'll use them step by step as we build our scripts.
3. Creating Your First Shell Script
Step 1 — Plan Before You Type
Before writing a single line of code, always write down the exact steps and commands your script needs to execute. This is a habit that separates good scripters from frustrated ones.
For our Smart Home morning startup routine, the steps are:
1. Create a folder for today's automation session
2. Add the living room lights device
3. Power on the hub
4. Connect all devices to the network
5. Run a device health check
6. Activate the daily schedule
7. Check and display the system status
Step 2 — Write the Script
Open a terminal and create a new file:
vi setup_smarthome.sh
Inside this file, type the following:
#!/bin/bash
# Smart Home Morning Startup Script
mkdir living_room_session
smarthome add living_room_lights
smarthome start hub
smarthome connect network
smarthome check devices
smarthome activate schedule
smarthome_status=$(smarthome status)
echo "System Status: $smarthome_status"
The Shebang Line — #!/bin/bash
The very first line of every shell script should be:
#!/bin/bash
This is called the shebang (pronounced "shuh-bang"). It tells the operating system: "Use the Bash shell to execute this file." Without it, the system might not know how to run your script. Always include it.
Step 3 — Make the Script Executable
After creating the file, you can't run it yet. Try it:
bash setup_smarthome.sh
Wait — why can't we just type setup_smarthome.sh directly? Because the operating system only recognizes files as executable commands when:
The execute permission bit is set on the file
The directory containing the script is listed in the
PATHenvironment variable
Setting Execute Permission
Check the current permissions:
ls -l setup_smarthome.sh
You'll see something like:
-rw-r--r-- 1 user user 245 Apr 1 10:00 setup_smarthome.sh
Notice there's no x (execute) bit. Add it using chmod:
chmod +x setup_smarthome.sh
Now run it:
./setup_smarthome.sh
Adding to the PATH
If you want to run your script from anywhere without typing the full path, add its directory to the PATH variable:
export PATH=$PATH:/home/youruser
Using $PATH instead of typing the full existing path is important — it appends your directory to whatever already exists, rather than replacing it (which would break all your other commands).
Now you can run:
setup_smarthome.sh
From any directory. You can also verify where the script is located:
which setup_smarthome.sh
Best Practices for Naming Scripts
| ❌ Bad Name | ✅ Good Name |
|---|---|
script1.sh |
setup_smarthome.sh |
my_script.sh |
morning_startup.sh |
test.sh |
check_device_health.sh |
Name your scripts so that a stranger instantly understands what they do. If you plan to use the script as a runnable command, you can drop the .sh extension. If it's more of an internal utility, keep it.
4. Variables — Stop Hardcoding Values
The Problem With Hardcoding
Look at our script — the name living_room_lights appears in multiple places. What if tomorrow we want to run the same startup routine for bedroom_lights? We'd have to hunt through every line and change every occurrence. Miss one, and the script breaks in unexpected ways.
This is what we call hardcoding — baking a specific value directly into the code — and it's bad practice.
What Is a Variable?
A variable is a named container that holds a value. Instead of writing living_room_lights everywhere, you write the name once, store it in a variable, and reference the variable everywhere else.
Declaring and Using Variables
Here's how to define a variable:
device_name=living_room_lights
⚠️ No spaces around the
=sign.device_name = living_room_lightswill cause an error.
To use (or "expand") a variable, prefix it with $:
echo $device_name
Or inside a string:
echo "Setting up device: $device_name"
Updated Script Using Variables
#!/bin/bash
# Smart Home Morning Startup Script
device_name=living_room_lights
mkdir ${device_name}_session
smarthome add $device_name
smarthome start hub
smarthome connect network
smarthome check devices
smarthome activate schedule
system_status=$(smarthome status)
echo "System Status for \(device_name: \)system_status"
Now to run the same script for bedroom_thermostat, you only change one line at the top. Everything else stays the same.
Storing Command Output in a Variable
Notice this line:
system_status=$(smarthome status)
The $(...) syntax is called command substitution. It runs the command inside and stores whatever it prints into the variable. This is extremely powerful — you can capture any command's output and use it later.
Variable Naming Rules
| Rule | Example |
|---|---|
| Only letters, numbers, and underscores | device_name ✅ |
| No hyphens | device-name ❌ |
| Case-sensitive | Device_Name ≠ device_name |
| Use lowercase with underscores (best practice) | living_room_status ✅ |
Good variable names:
device_name=thermostat
room_id=bedroom
Bad variable names:
DeviceName=thermostat # avoid CamelCase
device-name=thermostat # hyphens not allowed
d=thermostat # too cryptic
5. Command Line Arguments — Making Scripts Flexible
The Problem We're Solving
Even with variables, our script still requires you to edit the file every time you want to run it for a different device. That's still inconvenient.
Wouldn't it be much better to do this?
./setup_smarthome.sh bedroom_thermostat
And the script automatically knows the device name is bedroom_thermostat — without you ever opening the file? That's exactly what command line arguments enable.
How Arguments Work
When you run a script, everything you type after the script name is broken into numbered positional parameters:
| What You Type | Variable Inside Script |
|---|---|
| The script name itself | $0 |
| First argument | $1 |
| Second argument | $2 |
| Third argument | $3 |
| ... and so on | \(4, \)5, ... |
For example, running:
./setup_smarthome.sh bedroom_thermostat
Inside the script:
$0=./setup_smarthome.sh$1=bedroom_thermostat
Updated Script With Command Line Arguments
#!/bin/bash
# Smart Home Morning Startup Script
device_name=$1
mkdir ${device_name}_session
smarthome add $device_name
smarthome start hub
smarthome connect network
smarthome check devices
smarthome activate schedule
system_status=$(smarthome status)
echo "System Status for \(device_name: \)system_status"
Now you can run:
./setup_smarthome.sh living_room_lights
./setup_smarthome.sh bedroom_thermostat
./setup_smarthome.sh front_door_camera
Each run uses a different device — the script itself never changes.
Why Not Just Use $1 Directly?
You could replace device_name=\(1 and just use \)1 throughout the script:
mkdir ${1}_session
smarthome add $1
echo "Status for \(1: \)system_status"
This works, but it's harder to read. Six months later, when you or a teammate opens this script, \(1 is meaningless. \)device_name tells you exactly what it represents.
Best practice: Assign \(1, \)2, etc. to descriptively named variables at the top of your script, then use those variables throughout.
Design Your Scripts to Be Reusable
"Always develop the script keeping in mind that it is to be reused by another person who has not developed the script — or probably doesn't even have experience working with shell scripts."
A reusable script:
Takes inputs via command line arguments (not hardcoded values)
Has meaningful variable names
Requires zero modification before each run
6. Read Inputs — Interactive Prompts
Another Way to Get Input
Command line arguments are great for automation. But sometimes you want your script to ask the user a question while it's running — like a wizard or setup tool.
The read command lets you do exactly that.
Basic Syntax
read variable_name
When the script hits this line, it pauses and waits for the user to type something. Whatever they type gets stored in variable_name.
Example
#!/bin/bash
read device_name
smarthome add $device_name
echo "Device $device_name has been added."
This works, but there's a problem: when the script pauses, the terminal just shows a blinking cursor. The user has no idea what they're supposed to type.
Adding a Prompt Message
Use the -p flag to display a message before waiting for input:
#!/bin/bash
read -p "Enter the device name to set up: " device_name
smarthome add $device_name
echo "Device $device_name has been set up successfully."
Now the user sees:
Enter the device name to set up: _
Much friendlier.
When to Use read vs. Command Line Arguments
This is a common question, and the answer depends on how your script will be used:
| Situation | Recommended Approach |
|---|---|
| Script will be run manually by a person | read with a prompt is fine |
| Script needs confirmation before a destructive action | read (e.g., "Are you sure you want to delete this device? [y/n]") |
| Script will be called by another script (automation) | Command line arguments — read would freeze the automation |
| Menu-driven interactive programs | read |
| Scripts that will run on a schedule (cron jobs) | Command line arguments — no human available to answer prompts |
The Best of Both Worlds
Ideally, a well-designed script checks whether a command line argument was provided. If yes, it uses that. If not, it prompts the user. We'll cover the conditional logic needed for this when we get to if statements in a later chapter.
7. Arithmetic Operations — Doing Math in Shell Scripts
Why Arithmetic in Shell Scripts?
You might want to count devices, calculate percentage usage, track elapsed time, or compute threshold values. Shell scripting supports math — though with a few quirks to be aware of.
Method 1 — The expr Command
expr is a basic utility for evaluating expressions:
expr 6 + 3 # Output: 9
expr 10 - 4 # Output: 6
expr 8 / 2 # Output: 4
expr 5 \* 3 # Output: 15
⚠️ Important rules for
expr:
Operators and numbers must be separated by spaces.
expr 6+3will not work.For multiplication, you must escape the with a backslash:
\*. This is because is a wildcard character in the shell.
Using Variables with expr
device_count=6
new_devices=3
total=\((expr \)device_count + $new_devices)
echo "Total devices: $total"
Output:
Total devices: 9
Method 2 — Double Parentheses (( ))
The double-parentheses syntax is more modern and much more intuitive:
echo $((6 + 3)) # 9
echo $((10 - 4)) # 6
echo $((8 / 2)) # 4
echo $((5 * 3)) # 15
Advantages over expr:
No need to escape for multiplication
Spaces between operator and values are optional
Variables inside don't need the
$prefixFeels more like "normal" programming math
Example With Variables
a=6
b=3
echo $((a + b)) # 9
echo $((a * b)) # 18
echo $((a - b)) # 3
C-Style Increment and Decrement
You can also use the familiar ++ and -- operators:
device_count=5
((device_count++))
echo $device_count # 6
((device_count--))
echo $device_count # 5
ℹ️ Note:
++aand--awork the same way as in C — the value is updated and then used.
Method 3 — The bc Utility (Floating Point Math)
Both expr and $(( )) have a significant limitation: they only work with whole numbers (integers). Division always gets truncated:
echo $((10 / 3)) # Output: 3 (not 3.333...)
For decimal results, use the bc utility (Basic Calculator):
echo "10 / 3" | bc -l # Output: 3.33333333333333333333
The -l flag tells bc to use its math library, which enables floating-point output.
Practical Example — Calculating Device Load Percentage
active_devices=7
total_devices=10
load_percentage=\((echo "scale=2; (\)active_devices / $total_devices) * 100" | bc -l)
echo "Current load: $load_percentage%"
Output:
Current load: 70.00%
scale=2 tells bc to give you 2 decimal places.
Arithmetic Methods — Quick Comparison
| Feature | expr |
$(( )) |
bc |
|---|---|---|---|
| Basic math | ✅ | ✅ | ✅ |
| Needs spaces | ✅ Required | ❌ Optional | ❌ |
| Multiplication | \* (escape needed) |
* (no escape) |
* |
| Floating point | ❌ | ❌ | ✅ |
| Variables (inside) | $var |
var (no $ needed) |
$var |
Putting It All Together — Our Smart Home Script
Here's a final version of our script incorporating everything we've learned in this chapter:
#!/bin/bash
# Smart Home Morning Startup Script
# Usage: ./setup_smarthome.sh <device_name>
# Get device name from command line argument, or prompt if not provided
device_name=$1
if [ -z "$device_name" ]; then
read -p "Enter the device name to set up: " device_name
fi
# Track session
session_dir="${device_name}_session"
mkdir $session_dir
# Add the device
smarthome add $device_name
# Run the startup sequence
smarthome start hub
smarthome connect network
smarthome check devices
smarthome activate schedule
# Capture and display status
system_status=$(smarthome status)
echo "System Status for \(device_name: \)system_status"
# Arithmetic — count total devices
total_devices=$(smarthome list | wc -l)
echo "Total devices managed: $total_devices"
This script:
Accepts a device name as a command line argument
Falls back to prompting the user if none is provided
Uses meaningful variable names throughout
Captures command output into variables
Displays a running device count
Chapter 1 Summary
Here's a quick recap of everything covered:
| Topic | Key Takeaway |
|---|---|
| What Shell Scripts Are | Text files of Linux commands, executed automatically by the shell |
| Creating a Script | Use any text editor; always start with #!/bin/bash |
| Making It Executable | chmod +x scriptname.sh; add directory to $PATH to run it like a command |
| Variables | Store values with name=value; use them with \(name; capture command output with \)(command) |
| Command Line Arguments | Access via \(1, \)2, etc.; assign to named variables for readability |
| Read Inputs | Use read -p "prompt" variable for interactive user input |
| Arithmetic | Use expr or $(( )) for integers; use bc -l for decimals |
What's Next?
In the next chapter, we'll explore Control Flow — making your scripts smart enough to make decisions. We'll cover if/else statements, comparison operators, and loops (for and while). This is where scripting really starts to feel like programming.
Until then, practice the concepts from this chapter. Try building a small script that automates something repetitive on your own system. Even something simple — like creating a folder structure or checking disk usage — is a fantastic start.
Happy scripting! 🖥️
This blog is part of the Shell Scripts for Beginners series. Each post corresponds to a chapter of the course, with hands-on examples designed to take you from zero to confident scripter.