Linux L5: Bash Scripting on Linux

Write Bash scripts that use variables, loops, and arguments to automate real tasks.

Introduction

30 Second Summary

You have saved commands into scripts before, but those scripts run the same lines top to bottom every time. What if your scripts could remember information, make decisions, and repeat work on their own?

In this project, you will write Bash scripts on your Linux machine that go far beyond running commands in order. You will teach your scripts to store data in variables, make choices with conditionals, and repeat tasks with loops.

What You'll Build

Hand your inspect script any filename and instantly see whether the file exists, how many lines it has, and how big it is.

By the end of this project, you'll have:

  • A greeter script that asks your name and favorite Linux tool, then replies with a personalized message using variables and user input.
  • Scripts that check whether files exist before acting and loop through entire folders to back up every file automatically.
  • A reusable inspect tool that takes any filename you give it and reports its line count and size on the spot.
  • Secret Mission: Build a System Reporter v2 that writes a dated health report, warns about disk usage, and reports folder sizes.

Are there any prerequisites?

This project is part of a beginner Linux series and builds on the first four projects. You should already be comfortable with basic terminal commands, nano, file permissions, pipes, and redirection.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters. You have been typing commands one at a time, but in this project you will teach your computer to run those commands on its own, make decisions, and repeat work without being told twice.

Write Your First Real Script

Every command you have typed in the terminal so far vanished after it ran. If you wanted to repeat it, you had to type it again from memory.

A script saves your commands in a file so you can replay them with a single word. Think of it like writing down a recipe instead of cooking from memory. In this step, you will create a dedicated folder for your scripts and write your first two.

In this step, get ready to:
  • Create a ~/scripts/ directory for all your project scripts.
  • Write and run hello.sh to print a greeting message.
  • Build first.sh with your name, today's date, and a motivational message.
Write and run a hello script the simple way

Every script you write in this project will live in the same place. A dedicated directory keeps your work organized and easy to find.

  • In your terminal, create the scripts directory and move into it by running these commands:
mkdir -p ~/scripts
cd ~/scripts

What does mkdir -p do?

mkdir -p creates the directory if it does not exist and does nothing if it already exists. This makes the command safe to run more than once.

  • Confirm you are in the right place by running:
pwd

You should see a path ending in /scripts.

Now it is time to write your first script. A Bash script is a plain text file containing commands that Linux runs in order, top to bottom.

  • Open nano to create your hello script by running:
nano hello.sh
  • Type the following script exactly into nano:
#!/bin/bash
# My very first real script
echo "Hello from a script!"

What does this script do?

  • #!/bin/bash is the shebang line. It is always the first line of a Bash script and tells Linux which program should read and run the file. Without it, Linux has to guess.
  • # on the second line starts a comment. Comments are notes for humans. Bash ignores everything after #.
  • echo prints text to the screen. Whatever you put in quotes after it appears in the terminal.
  • Save the file by pressing Ctrl+O, then Enter.
  • Exit nano by pressing Ctrl+X.
  • Run your script by telling Bash to read it directly:
bash hello.sh

You should see Hello from a script! printed in the terminal. This works because you are explicitly telling Bash to read the file.

Script prints nothing?

  • Make sure you saved the file in nano before exiting. Press Ctrl+O, then Enter to save, then Ctrl+X to exit.
  • Check that you typed echo at the start of the third line. Without it, the script has nothing to print.
  • Help me debug my hello.sh script

✔️ Awesome, I've got everything!

Great. Double check you have saved hello.sh.

ⓧ I'd like to double check the full code

Here is the full hello.sh file for comparison:

#!/bin/bash
# My very first real script
echo "Hello from a script!"
Make the script executable and run it the real way

Running a script with bash hello.sh works, but there is a more professional way. The ./ prefix tells Linux to run the file directly as a program.

Before you run the next command, think about what will happen. You have not changed any file permissions yet. Do you think Linux will let you run hello.sh as a program?

  • Try running your script the professional way:
./hello.sh

You see a Permission denied error. This is expected. The file does not have execute permission yet, so Linux refuses to run it as a program. You already know how to fix this from your earlier work with chmod.

  • Add execute permission to your script by running:
chmod +x hello.sh
  • Run the script again:
./hello.sh

This time you see Hello from a script! printed. Two things made this work together: chmod +x gave the file execute permission, and the shebang line told Linux to use Bash to run it.

  • Verify the execute permission is set by running:
ls -l hello.sh

What should I see?

You should see something like -rwxr-xr-x at the start of the line. The x characters confirm execute permission is set.

Why two ways to run a script?

bash hello.sh works without execute permission because you are telling Bash to read the file yourself. The ./hello.sh approach asks Linux to figure out how to run the file on its own. Linux checks the shebang line, finds #!/bin/bash, and launches Bash for you.

The ./ approach is the standard way you will see scripts run in the real world. From now on, every script in this project uses it.

Still seeing Permission denied?

  • Make sure you typed chmod +x hello.sh with a space between +x and hello.sh.
  • Check that you are in the ~/scripts/ directory. Run pwd to confirm.
  • Help me fix Permission denied on my script
Write first.sh with your name and today's date

Your hello.sh prints the same static message every time. Your next script will pull live data from the system using command substitution.

The $(date) syntax runs the date command and pastes its output directly into your echo line. The shell runs the command inside $() and substitutes the result.

  • Open nano to create your new script by running:
nano first.sh
  • Type the following script exactly into nano:
#!/bin/bash
# My first real script - prints a personal greeting

echo "Hello! My name is a NextWork learner."
echo "Today's date is: $(date)"
echo "You are doing amazing things. Keep going!"

What does this script do?

  • The first echo prints a static greeting with your name.
  • The second echo uses $(date) to fetch the current date and time from your system. This value changes every time the script runs.
  • The third echo prints a motivational message. Three lines in, three lines out.
  • Save the file by pressing Ctrl+O, then Enter.
  • Exit nano by pressing Ctrl+X.
  • Make the script executable by running:
chmod +x first.sh

Before you run your script, predict what you will see. How many lines of output do you expect? What will the second line show?

  • Run your new script:
./first.sh

You should see three lines printed: your name, today's full date and time (generated live by the script), and a motivational message. The date line changes every time you run the script because $(date) fetches the current time.

Not seeing a live date?

  • If you see the literal text $(date) printed instead of a real date, check that you used regular double quotes (") and not single quotes ('). Single quotes prevent command substitution.
  • If you get Permission denied, make sure you ran chmod +x first.sh before running the script.
  • Help me debug my first.sh script output

✔️ Awesome, I've got everything!

Great. Double check you have saved both hello.sh and first.sh.

ⓧ I'd like to double check the full code

Here is the full first.sh file for comparison:

#!/bin/bash
# My first real script - prints a personal greeting

echo "Hello! My name is a NextWork learner."
echo "Today's date is: $(date)"
echo "You are doing amazing things. Keep going!"

Your scripts folder is set up and you have two working scripts. Next up, you will learn to store information in variables so your scripts can remember things and adapt.

Store Data in Variables

In Step 1, you wrote first.sh and ran it with a single command. The script works, but it prints the exact same message every time. If you want to change even one word, you have to open the file and edit it.

Variables fix this. A variable is a named container that holds a value, so you can store something once and use it wherever you need. In this step, you will create variables, capture live command output, and read input directly from the user.

In this step, get ready to:
  • Store data in variables and print it to the screen.
  • Capture user input with read -p.
  • Build a greeter script that asks two questions and replies with a personalized message.
Create variables and use them

Think of a variable as a labeled box. You write a name on the outside, put a value inside, and later use $ to open the box and get the value out.

The golden rule of variables

NO spaces around the = sign. NAME="Alice" is correct. NAME = "Alice" with spaces causes a "command not found" error because Bash treats NAME as a command to run.

  • Create a new file called variables.sh by opening it in nano:
nano variables.sh
  • Store a greeting and today's day name in variables by typing this script:
#!/bin/bash
# Demonstrating variables and command substitution
GREETING="Hello, world"
echo "$GREETING"
TODAY=$(date +%A)
echo "Today is $TODAY"

What does this script do?

  • GREETING="Hello, world" creates a variable named GREETING and stores the text inside it.
  • echo "$GREETING" uses $ to open the box and print what is inside.
  • $(date +%A) runs the date command and captures the full weekday name (like "Wednesday"). This is called command substitution. The shell runs whatever is inside $() and pastes the result in its place.
  • TODAY stores that captured output so you can reuse it later.
  • Save the file by pressing Ctrl+O, then Enter.
  • Exit nano by pressing Ctrl+X.
  • Make the script executable by running:
chmod +x variables.sh
  • Run your new script by running:
./variables.sh

You should see Hello, world on the first line and Today is followed by the current day name (like "Wednesday") on the second line.

Seeing "command not found" or wrong output?

  • If you see a "command not found" error, you probably have spaces around the = sign. Open the file with nano variables.sh and make sure GREETING="Hello, world" has no spaces before or after =.
  • If the script prints the word GREETING instead of Hello, world, you forgot the $ sign. It should be echo "$GREETING", not echo "GREETING".
  • Still stuck? Help me figure out why my variables.sh script is not printing the right output.

✔️ Awesome, I've got everything!

Make sure you have saved variables.sh and that it runs without errors.

ⓧ I'd like to double check the full code

Your variables.sh should look exactly like this:

#!/bin/bash
# Demonstrating variables and command substitution
GREETING="Hello, world"
echo "$GREETING"
TODAY=$(date +%A)
echo "Today is $TODAY"
Read user input with read -p

So far, your variables hold values you typed into the script yourself. What if you want the script to ask the user a question and store the answer? That is what read -p does. It prints a prompt, waits for the user to type something, and stores the result in a variable.

  • Create a new file called askname.sh by opening it in nano:
nano askname.sh
  • Ask the user for their name and greet them by typing this script:
#!/bin/bash
# Ask for user input and greet them
read -p "What is your name? " NAME
echo "Hello, $NAME!"

What does this script do?

  • read -p "What is your name? " NAME prints the prompt text and waits. Whatever the user types gets stored in the NAME variable.
  • echo "Hello, $NAME!" opens the box with $ and prints a personalized greeting using whatever the user typed.
  • Save the file by pressing Ctrl+O, then Enter.
  • Exit nano by pressing Ctrl+X.
  • Make it executable and run it by running these commands:
chmod +x askname.sh
./askname.sh

The script prints What is your name? and waits. Type your name and press Enter. You should see a greeting with the name you typed.

Script not waiting for input?

  • Make sure there is a space between the prompt text and the closing quote: read -p "What is your name? " NAME. The space before the closing quote keeps the user's cursor from bumping up against the question mark.
  • If the greeting prints the literal word NAME instead of what you typed, add the $ sign before it in the echo line.
  • Still stuck? Help me debug my askname.sh script that should ask for a name and print a greeting.

✔️ Awesome, I've got everything!

Make sure you have saved askname.sh and that it greeted you by name when you ran it.

ⓧ I'd like to double check the full code

Your askname.sh should look exactly like this:

#!/bin/bash
# Ask for user input and greet them
read -p "What is your name? " NAME
echo "Hello, $NAME!"
Build a greeter that asks two questions

Time to combine what you have learned. You will build a greeter script that asks for your name AND your favorite Linux tool, then prints a personalized multi-line reply using both answers.

  • Create a new file called greeter.sh by opening it in nano:
nano greeter.sh

Your greeter script needs to do four things:

  • Ask for the user's name with read -p and store it in a variable.
  • Ask for their favorite Linux tool with a second read -p and store it in another variable.
  • Print a multi-line reply that uses both variables.
  • Include the current day name using $(date +%A).

Give it a shot using what you learned from variables.sh and askname.sh. Pick whichever tab matches how you feel.

✔️ I'll give it a go!

Use read -p twice to capture two pieces of input. Store them in variables named NAME and TOOL. Then use echo lines with $NAME, $TOOL, and $(date +%A) to build the reply. When you are done, save and exit nano.

ⓧ Maybe a little help...

Type the following script into nano:

#!/bin/bash
# Greeter script - asks for your name and favorite tool

read -p "What is your name? " NAME
read -p "What is your favorite Linux tool? " TOOL

echo ""
echo "Nice to meet you, $NAME!"
echo "Great choice - $TOOL is a powerful tool."
echo "Today is $(date +%A), and you are learning Bash scripting!"

What does this script do?

  • Two read -p lines ask for the user's name and favorite tool, storing them in NAME and TOOL.
  • echo "" prints a blank line to separate the questions from the reply.
  • The three echo lines use $NAME, $TOOL, and $(date +%A) to build a personalized reply with live data.

Save and exit nano when you are done typing.

  • Make your greeter executable by running:
chmod +x greeter.sh

Before you run this, what do you expect the script to print first: a greeting or a question?

  • Run your greeter by running:
./greeter.sh

The script asks for your name first. Type your name and press Enter. Then it asks for your favorite Linux tool. Type a tool name and press Enter again. You should see a multi-line reply that includes both your answers and the current day name.

Greeter not working as expected?

  • If the reply shows literal variable names like $NAME instead of what you typed, check that your echo lines use double quotes ("), not single quotes ('). Single quotes prevent Bash from replacing variables with their values.
  • If you see a "Permission denied" error, you may have forgotten to run chmod +x greeter.sh before running the script.
  • Still stuck? Help me debug my greeter.sh script that uses read -p and variables.

Your scripts can now store data, capture live command output, and ask the user for input. Next up, you will teach your scripts to make decisions with if/else so they can check what is real before acting.

Make Decisions with if/else

Your greeter.sh can ask questions and remember answers using variables. But right now, every script you have written runs every command blindly. If a file goes missing, the script crashes or prints confusing errors with no way to handle the situation.

Conditionals let a script check a condition and choose a different path depending on what it finds. Think of an if statement as a fork in the road. In this step, you will teach your script to look before it leaps.

In this step, get ready to:
  • Discover how exit codes tell you whether a command succeeded or failed.
  • Write a script that checks whether a file exists using if/else.
  • Test the script both ways to prove it takes different paths.
Discover exit codes

Every command you run in the terminal returns a secret number when it finishes. This number is called an exit code. An exit code of 0 means "it worked." Anything else means "something went wrong."

  • See a successful exit code by listing a file you know exists:
ls hello.sh
  • Check the exit code of that command by running:
echo $?

You see 0. The ls command found the file, so it returned 0 to say "success."

  • Now try listing a file that does not exist:
ls fakefile.txt
  • Check the exit code again:
echo $?

This time you see a non-zero number (like 2). The command failed, so it returned a non-zero code to say "something went wrong."

What are exit codes?

Every command sends back a number when it finishes. Zero means success. Any other number means failure. You never see this number unless you ask for it with echo $?.

The if statement uses exit codes behind the scenes to decide which path to take. When the condition succeeds (exit code 0), the script goes one way. When it fails (non-zero), the script goes the other way.

Write checkfile.sh with if/else

Now you know how exit codes work. The if statement is how your script acts on them automatically. It checks a condition. If the condition passes, it runs one block of code. If not, it runs the other.

  • Create a new script by opening nano:
nano checkfile.sh
  • Type this script exactly into nano:
#!/bin/bash
# Check whether report.txt exists

if [ -f "report.txt" ]; then
    echo "report.txt exists! Ready to go."
else
    echo "report.txt does not exist. You may need to create it."
fi

What does this code do?

  • if [ -f "report.txt" ] checks whether a file called report.txt exists. The -f flag means "is this a file?" The spaces inside the square brackets are required.
  • then starts the "yes" path. Everything between then and else runs only when the file exists.
  • else starts the "no" path. Everything between else and fi runs only when the file is missing.
  • fi closes the if block. It is "if" spelled backwards. Every if needs a matching fi at the end.
  • You can also test for a directory instead of a file by using -d instead of -f.
  • Save the file in nano by pressing Ctrl+O, then Enter, then Ctrl+X to exit.
  • Make the script executable by running:
chmod +x checkfile.sh

Seeing a syntax error?

  • "syntax error near unexpected token" usually means you are missing a fi or a then. Double-check that both are present.
  • The spaces inside the square brackets are required. [ -f "file" ] works, but [-f "file"] does not.
  • Make sure you used -f for file tests, not =. The = operator compares strings, not file existence.

Still stuck? Ask for help here.

✔️ Awesome, I've got everything!

Great. Double check you have saved checkfile.sh and made it executable.

ⓧ I'd like to double check the full code

Here is the complete checkfile.sh for comparison. Make sure your version matches exactly.

#!/bin/bash
# Check whether report.txt exists

if [ -f "report.txt" ]; then
    echo "report.txt exists! Ready to go."
else
    echo "report.txt does not exist. You may need to create it."
fi
Test checkfile.sh both ways

To prove the script actually takes different paths, you will run it twice. Once with report.txt present, and once with it removed.

  • Create a test file called report.txt by running:
echo "test data" > report.txt

Before you run checkfile.sh, report.txt now exists in your ~/scripts/ folder. Which message do you think the script will print?

  • Run the script to find out:
./checkfile.sh

You see: report.txt exists! Ready to go. The script found the file and took the "yes" path.

  • Now remove report.txt by running:
rm report.txt

The file is gone now. What do you think the script will say this time?

  • Run the script again to confirm:
./checkfile.sh

You see: report.txt does not exist. You may need to create it. The script took the "no" path this time. Same script, two completely different outcomes.

Why is this useful?

Without an if statement, a script that tried to read a missing file would crash with a messy error. With if/else, your script can check first and handle both situations gracefully. The better your scripts get at handling the unexpected, the more you can trust them to run on their own.

Your scripts can now make decisions. That is a huge step forward. But checkfile.sh only checks one file at a time. What if you needed to process every .txt file in a folder? Next up, you will build a loop that repeats work automatically so you never have to run the same command over and over.

Repeat Work with Loops

Your checkfile.sh script can look at a file and decide what to do. That is a real superpower. But it handles one file at a time. If you had 50 text files to check, you would need to run the script 50 times.

Loops fix this. A loop is like a tireless robot. You write the instructions once, hand the loop a list of items, and it repeats the work for each one.

In this step, get ready to:
  • Write a for loop that repeats an action for each item in a list.
  • Write a while loop that counts using a variable.
  • Build a backup script that copies every .txt file into a backup folder.
Write a for loop over a word list

A for loop picks one item from a list, stores it in a variable, runs a block of code, then picks the next item. It keeps going until every item in the list has had its turn.

  • Create a new script file by running:
nano countfruit.sh
  • Type this script into the editor:
#!/bin/bash
# For loop over a word list
for FRUIT in apple banana cherry; do
    echo "I like $FRUIT"
done

What does this code do?

  • for FRUIT in apple banana cherry creates a list of three words. The loop picks the first word (apple), stores it in the variable FRUIT, and runs everything between do and done. Then it picks the next word and repeats.
  • The indentation inside the loop is for humans, not required by Bash. It makes the script much easier to read.
  • Save and exit nano by pressing Ctrl+O, Enter, then Ctrl+X.
  • Make the script executable and run it by running these commands:
chmod +x countfruit.sh
./countfruit.sh

You should see three lines printed, one for each fruit in the list:

I like apple
I like banana
I like cherry

Seeing a syntax error?

  • "syntax error near unexpected token" usually means you are missing done at the end of the loop. Open the file with nano countfruit.sh and check that done is on its own line after the echo line.
  • Make sure the line ends with do after the semicolon. The full line should read for FRUIT in apple banana cherry; do.
  • help me fix a syntax error in my for loop script

✔️ Awesome, I've got everything!

Great. Double check you have saved countfruit.sh before moving on.

ⓧ I'd like to double check the full code

#!/bin/bash
# For loop over a word list
for FRUIT in apple banana cherry; do
    echo "I like $FRUIT"
done

For loops also work on files. Instead of typing a word list, you can use a pattern like *.txt and the loop will run once for every matching file.

  • Create a few test .txt files by running these commands:
echo "Meeting notes from Monday" > notes.txt
echo "Pick up groceries" > todo.txt
echo "Learn Bash scripting" > ideas.txt
  • Confirm the files were created by running:
ls *.txt

You should see ideas.txt, notes.txt, and todo.txt listed in the output.

  • Run a for loop directly in the terminal to see it work on files:
for f in *.txt; do echo "Found: $f"; done

The loop prints one line for each .txt file it found. You will use this same pattern inside a script later in this step.

Write a while counter loop

A for loop walks through a fixed list. A while loop is different. It keeps running as long as a condition is true, like a runner who keeps going until they hit a wall.

  • Create a new script file by running:
nano counter.sh
  • Type this script into the editor:
#!/bin/bash
# While loop counting from 1 to 5
COUNT=1
while [ "$COUNT" -le 5 ]; do
    echo "Count: $COUNT"
    (( COUNT++ ))
done

What does this code do?

  • COUNT=1 creates a counter variable starting at 1.
  • while [ "$COUNT" -le 5 ] means "keep looping as long as COUNT is less than or equal to 5." The -le stands for "less than or equal."
  • (( COUNT++ )) adds 1 to COUNT each time the loop runs. Without this line, the loop would run forever because COUNT would never reach 6.
  • Save and exit nano by pressing Ctrl+O, Enter, then Ctrl+X.
  • Make the script executable and run it by running these commands:
chmod +x counter.sh
./counter.sh

You should see five lines printed, counting from 1 to 5:

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5

Loop never stops or prints nothing?

  • If the loop runs forever and keeps printing numbers, you may be missing the (( COUNT++ )) line. Press Ctrl+C to stop it, then open the file with nano counter.sh and add the missing line.
  • If you see a syntax error, check that you have spaces inside the square brackets. It must be [ "$COUNT" -le 5 ] with spaces, not ["$COUNT" -le 5].
  • help me debug my while loop in Bash

✔️ Awesome, I've got everything!

Great. Double check you have saved counter.sh before moving on.

ⓧ I'd like to double check the full code

#!/bin/bash
# While loop counting from 1 to 5
COUNT=1
while [ "$COUNT" -le 5 ]; do
    echo "Count: $COUNT"
    (( COUNT++ ))
done
Build a backup script

Time to put your for loop skills to work on a real task. You will write a script that automatically copies every .txt file in the current folder into a backups/ directory.

  • Create the backup script by running:
nano backup.sh
  • Type this script into the editor:
#!/bin/bash
# Backup all .txt files with a .bak extension

mkdir -p backups

for f in *.txt; do
    cp "$f" "backups/${f}.bak"
    echo "Backed up: $f -> backups/${f}.bak"
done

echo "All .txt files backed up!"

What does this code do?

  • mkdir -p backups creates a backups/ directory if it does not already exist. The -p flag prevents an error if the folder is already there.
  • for f in *.txt loops over every .txt file in the current directory, storing each filename in the variable f.
  • cp "$f" "backups/${f}.bak" copies the file into backups/ and adds a .bak extension. The quotes around "$f" protect filenames that contain spaces.
  • Save and exit nano by pressing Ctrl+O, Enter, then Ctrl+X.
  • Make the script executable by running:
chmod +x backup.sh

Before you run this, think about what you expect to see. You created three .txt files earlier. How many files will the script back up? What will the filenames look like in the backups/ folder?

  • Run the backup script by running:
./backup.sh

You should see a confirmation line for each file, followed by a final message:

Backed up: ideas.txt -> backups/ideas.txt.bak
Backed up: notes.txt -> backups/notes.txt.bak
Backed up: todo.txt -> backups/todo.txt.bak
All .txt files backed up!
  • Confirm the backup copies exist by running:
ls backups/

You should see .bak copies of every .txt file listed in the output.

Not seeing your backup files?

  • If the script printed Backed up: *.txt as a literal line instead of real filenames, there are no .txt files in your current directory. Run ls *.txt to check, and make sure you are in the ~/scripts/ directory where you created the test files.
  • If you see a syntax error, open backup.sh in nano and check that done appears on its own line after the echo line inside the loop.
  • If filenames with spaces cause errors, make sure you have quotes around "$f" in both the cp and echo lines.
  • help me fix my backup.sh script

✔️ Awesome, I've got everything!

Great. Double check you have saved backup.sh before moving on.

ⓧ I'd like to double check the full code

#!/bin/bash
# Backup all .txt files with a .bak extension

mkdir -p backups

for f in *.txt; do
    cp "$f" "backups/${f}.bak"
    echo "Backed up: $f -> backups/${f}.bak"
done

echo "All .txt files backed up!"

Your scripts can now repeat work on their own. Next up, you will make your scripts even more flexible by accepting arguments so one script can work on any file you give it.

Accept Arguments to Make Scripts Reusable

In Step 4, you built a loop that processed every .txt file in your folder automatically. Your backup.sh does the work of a dozen copy commands in a single run.

There is still one limitation. Every script you have written so far has file names baked right into the code. Want to check a different file? You have to open nano and edit the script every time. Arguments solve this by letting you pass information to a script the moment you run it, so one script works on any file you hand it.

In this step, get ready to:
  • Learn how positional parameters let a script receive outside information.
  • Build inspect.sh, a reusable file inspector that accepts any filename.
  • Test the inspector with both a valid file and a missing argument.
Learn what $1, $2, and $# do

When you type extra words after a script's name, Bash automatically slots those words into numbered containers called positional parameters. Think of them as numbered mailboxes at the front door of your script.

If you run ./script.sh hello world, Bash puts hello into $1 and world into $2. A special variable called $# holds the total count of arguments you passed in.

  • Create a demo script to see this in action by running:
nano argdemo.sh
  • Type the following script in nano:
#!/bin/bash
# Demo: positional parameters
echo "First argument: $1"
echo "Second argument: $2"
echo "Number of arguments: $#"

What does this script do?

  • $1 grabs the first word you type after the script name.
  • $2 grabs the second word.
  • $# counts how many arguments were passed in total.
  • Save and exit nano by pressing Ctrl+O, then Enter, then Ctrl+X.
  • Make the script executable by running:
chmod +x argdemo.sh
  • Run the script with two words after its name by running:
./argdemo.sh hello world

You should see three lines printed: First argument: hello, Second argument: world, and Number of arguments: 2.

Try different arguments

Run ./argdemo.sh just-one and notice that $2 prints nothing because there is no second argument. The slot is simply empty.

Not seeing any output?

  • If you see Permission denied, you forgot to run chmod +x argdemo.sh first.
  • If you see command not found, make sure you included the ./ prefix and that you are in the ~/scripts/ directory.
  • help me figure out why my argdemo.sh script is not running
Guard against missing arguments

What happens if someone runs your script without giving it any arguments? $1 would be empty, and any command that depends on it would fail in confusing ways.

You can combine the if statement from Step 3 with $# to catch this early. If the argument count is zero, the script prints a helpful usage message and stops before anything breaks.

  • Start building your file inspector by opening a new file in nano:
nano inspect.sh
  • Type the following script, which checks whether the user provided an argument:
#!/bin/bash
# Inspect a file: check existence, line count, and size

if [ $# -eq 0 ]; then
    echo "Usage: ./inspect.sh <filename>"
    echo "Please provide a filename to inspect."
    exit 1
fi

What does this code do?

  • if [ $# -eq 0 ] checks whether the number of arguments is equal to zero. -eq stands for "equal" in Bash number comparisons.
  • The two echo lines print a friendly usage hint so the user knows what to type next time.
  • exit 1 stops the script immediately with an exit code of 1 (meaning something went wrong). Without it, the script would keep running into the code below and produce confusing errors.
  • Save and exit nano (Ctrl+O, Enter, Ctrl+X).
  • Make the script executable by running:
chmod +x inspect.sh
  • Test the safety check by running the script with no arguments:
./inspect.sh

You should see the usage message: Usage: ./inspect.sh <filename> followed by Please provide a filename to inspect.

Why exit 1 instead of just exit?

Every command returns an exit code. exit 0 means "everything is fine." exit 1 means "something went wrong." Using exit 1 after a missing argument tells other programs (and you) that the script did not finish successfully.

Seeing a syntax error?

  • Make sure there are spaces inside the square brackets: [ $# -eq 0 ] not [$# -eq 0].
  • Check that you typed fi on its own line at the bottom of the script to close the if block.
  • help me fix a syntax error in my inspect.sh argument check
Complete the file inspector

The argument guard is in place. Now add the logic that actually inspects the file. The script will store the filename, check whether the file exists, and report its line count and size.

  • Open inspect.sh again in nano by running:
nano inspect.sh
  • Use the arrow keys to move your cursor to the end of the file, right after the fi line.
  • Press Enter to create a blank line, then type the following code:
FILE="$1"

if [ -f "$FILE" ]; then
    LINES=$(wc -l < "$FILE")
    SIZE=$(du -h "$FILE" | cut -f1)
    echo "File: $FILE"
    echo "Lines: $LINES"
    echo "Size: $SIZE"
else
    echo "Error: '$FILE' does not exist."
    exit 1
fi

What does this code do?

  • FILE="$1" copies the first argument into a variable called FILE. This makes the rest of the script easier to read.
  • if [ -f "$FILE" ] checks whether the file actually exists on disk, just like checkfile.sh did in Step 3.
  • wc -l < "$FILE" counts the lines. The < feeds the file in as input so wc only prints the number, not the filename.
  • du -h "$FILE" | cut -f1 gets the file size in human-readable format and cut -f1 trims away the filename so only the size remains.
  • Save and exit nano (Ctrl+O, Enter, Ctrl+X).
  • Create a test file to inspect by running:
echo "This is a test report" > report.txt

Before you run inspect.sh, think about what you expect to see. Will ./inspect.sh report.txt show the file's information, or will it show the usage message? Why?

  • Test the inspector with a real file by running:
./inspect.sh report.txt

You should see three lines showing the file name, line count, and size. Since report.txt exists and contains one line, the output shows that information.

  • Now confirm the safety check still works by running the script with no arguments:
./inspect.sh

You should see the friendly usage message instead of any file information. Your script now takes different paths depending on what it receives.

Seeing an error instead of file info?

  • If you see unary operator expected, make sure $FILE is wrapped in double quotes: "$FILE". Unquoted variables cause problems when they are empty.
  • If wc -l prints a number AND the filename together, you may have typed wc -l "$FILE" instead of wc -l < "$FILE". The < is important.
  • If the script keeps running past the usage message when you give no arguments, check that you typed exit 1 on its own line after the second echo.
  • help me debug my inspect.sh script

✔️ Awesome, I've got everything!

Great. Double check you have saved inspect.sh and confirmed both test results.

ⓧ I'd like to double check the full code

Here is the complete inspect.sh file. Compare it with yours in nano and fix any differences.

#!/bin/bash
# Inspect a file: check existence, line count, and size

if [ $# -eq 0 ]; then
    echo "Usage: ./inspect.sh <filename>"
    echo "Please provide a filename to inspect."
    exit 1
fi

FILE="$1"

if [ -f "$FILE" ]; then
    LINES=$(wc -l < "$FILE")
    SIZE=$(du -h "$FILE" | cut -f1)
    echo "File: $FILE"
    echo "Lines: $LINES"
    echo "Size: $SIZE"
else
    echo "Error: '$FILE' does not exist."
    exit 1
fi

Secret mission

System Reporter v2

You have learned variables, conditionals, loops, and arguments as separate superpowers. Now combine all of them into one script that writes a dated system health report, checks disk usage with a warning threshold, and accepts an optional folder argument.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your scripts on your machine, tuck them away for later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.

Resources you used:

  • The ~/scripts/ directory containing all scripts (hello.sh, first.sh, variables.sh, askname.sh, greeter.sh, checkfile.sh, countfruit.sh, counter.sh, backup.sh, inspect.sh, sysreport.sh).
  • The backups/ subdirectory inside ~/scripts/ with .bak copies of your test files.
  • Test files (.txt files and report.txt) and the generated report file (sysreport-YYYY-MM-DD.txt) inside ~/scripts/.

Keep everything running

No action needed. Choose this if you want to keep practising, editing your scripts, or building new ones inside ~/scripts/.

  • Your scripts stay in ~/scripts/ and are ready to run any time you open a terminal.
  • You can add new scripts to the same folder as you learn more.
  • Everything is local. There are no services running in the background and no costs.

Pause - I'll come back to this later

Your scripts are just files on disk. There are no running processes or services to pause. You can leave them in place and come back whenever you are ready.

  • Clean up the generated report file and test files to save a small amount of disk space by running:
rm -f ~/scripts/*.txt
rm -rf ~/scripts/backups
  • Your .sh scripts are untouched and ready to run when you return.

Delete - I don't want to use this again

Remove the entire project directory and everything inside it. This deletes all scripts, test files, backups, and generated reports.

  • Delete the ~/scripts/ directory and all its contents by running:
rm -rf ~/scripts
  • Confirm the directory is gone by running:
ls ~/scripts
  • You should see a message saying the directory cannot be found. That confirms everything is deleted.

Nice Work!

Nice Work!

You just built an entire toolkit of Bash scripts from scratch on your Linux machine. Your ~/scripts/ directory is full of working programs that store data, make decisions, repeat work, and accept any file you hand them.

What you learned:

  • Wrote scripts with a shebang line, made them executable with chmod +x, and captured live data using command substitution with $().
  • Stored data in variables, read user input with read -p, and taught scripts to make decisions with if/else conditionals and file test operators.
  • Built for loops that process files automatically and made scripts reusable with positional arguments like $1 and $#.
  • Secret Mission: Combined every concept into a System Reporter that writes dated health reports with disk warnings and folder size summaries.
Bash Scripting Cheat Sheet

Keep this cheat sheet handy as a quick reference for every Bash scripting concept you learned in this project.

  • Shebang - First line of every script. Tells Linux to use Bash. Example: #!/bin/bash.
  • Comment - Notes for humans. Bash ignores everything after #. Example: # This is a comment.
  • echo - Prints text to the screen. Example: echo "Hello!".
  • Run with bash - Runs a script by telling Bash to read it. Example: bash script.sh.
  • Run with ./ - Runs a script directly (needs execute permission). Example: ./script.sh.
  • chmod +x - Adds execute permission to a file. Example: chmod +x script.sh.
  • Variable - A named container that holds a value. No spaces around =. Example: NAME="Alice".
  • Using a variable - Put $ before the name to get the value out. Example: echo "$NAME".
  • Command substitution - Runs a command and captures its output. Example: TODAY=$(date +%F).
  • read -p - Asks the user to type input and stores it. Example: read -p "Name? " NAME.
  • Exit code - A number every command returns. 0 means success. Example: echo $?.
  • if/else/fi - Takes a different path depending on a condition. Example: if [ -f "file" ]; then echo "yes"; else echo "no"; fi.
  • -f test - Checks whether a file exists. Example: [ -f "report.txt" ].
  • -d test - Checks whether a directory exists. Example: [ -d "backups" ].
  • Numeric comparison - Compares numbers. -eq (equal), -gt (greater than), -lt (less than). Example: [ "$COUNT" -gt 5 ].
  • for loop - Repeats a block once for each item in a list. Example: for f in *.txt; do echo "$f"; done.
  • while loop - Repeats a block as long as a condition is true. Example: while [ "$N" -le 5 ]; do echo "$N"; (( N++ )); done.
  • $1, $2 - The first and second arguments passed to a script. Example: ./script.sh hello world.
  • $# - The number of arguments passed to a script. Example: if [ $# -eq 0 ]; then echo "No args"; fi.
  • exit - Stops the script and returns an exit code. Example: exit 1.

Ready to quiz yourself?