Lesson 7 of 730 minutesHands-on

Write your first Bash script

A script is just a text file of commands that run one after another. If you’ve typed something twice, you can probably make it a script, and then it’s done the same way every time, however tired you are. In this last lesson you’ll write three: one that greets you, one that checks whether your websites are up, and one that backs up a folder. Then you’ll prove the backup works.

In this lesson

  • Write and run a script, with variables and arguments
  • Make it decide and repeat, using if and a loop
  • Back up a folder, test the restore, and fix the common errors

You’ll need: the terminal and the practice folder from the earlier lessons.

Try it yourself · part 1

Your first script

  1. Make a folder for your scripts and open a new file in nano:
    mkdir -p ~/scripts
    cd ~/scripts
    nano hello.sh
  2. Paste this in, then save with Ctrl + O and Enter, and leave with Ctrl + X:
    #!/bin/bash
    # Greet whoever is named on the command line
    name="$1"
    
    if [ -z "$name" ]; then
      echo "Usage: ./hello.sh yourname"
      exit 1
    fi
    
    echo "Hello, $name. Today is $(date +%A)."
  3. Give it permission to run, as in Lesson 5, and run it twice, without a name and with one:
    chmod +x hello.sh
    ./hello.sh
    ./hello.sh Sam
    Usage: ./hello.sh yourname
    Hello, Sam. Today is Sunday.

What each part does

LineWhat it does
#!/bin/bashThe first line says which program should run this file. It has to be the very first thing in it
# Greet...Anything after a # is a comment for humans, and Bash ignores it
name="$1"Creates a variable called name. $1 is the first thing typed after the script’s name
if [ -z "$name" ]-z tests whether it’s empty. then starts the block and fi, “if” backwards, ends it
exit 1Stops the script and reports failure. By convention, 0 means success and anything else means a problem
$(date +%A)Runs a command and puts its output right there in the text
Why ./? When you type a bare command like ls, the shell looks for it in a fixed list of system folders, and not in your current folder. ./hello.sh says exactly where this one is. After a script runs, echo $? prints its exit status, so try it after each of the runs above: you’ll see 1 and then 0.

Two rules that catch everyone

No spaces around the equals sign. city=Paris sets a variable, but city = Paris makes Bash look for a command called city, and you get command not found.

Double quotes fill in variables, and single quotes don’t. Try these at the prompt:

city=Paris
echo "I live in $city"
echo 'I live in $city'
I live in Paris
I live in $city

Put double quotes around your variables as a habit, as in "$name". Without them, a value containing a space falls apart into separate words, and that’s a common source of script bugs.

Try it yourself · part 2

A script that checks your websites

  1. Make a list of sites, one per line. Copy this block, paste it and press Enter. Use your own sites if you have some. The last one is meant to fail:
    cat > sites.txt <<'EOF'
    examineip.com
    tools.examineip.com
    this-domain-does-not-exist.example
    EOF
  2. Create the script, paste this in, save and leave:
    nano check-sites.sh
    #!/bin/bash
    # Check every site listed in sites.txt
    while read -r site; do
      code=$(curl -s -o /dev/null -m 10 -w "%{http_code}" "https://$site")
      if [ "$code" = "000" ]; then
        echo "$site: NO RESPONSE"
      else
        echo "$site: $code"
      fi
    done < sites.txt
  3. Make it runnable, and run it:
    chmod +x check-sites.sh
    ./check-sites.sh
    examineip.com: 200
    tools.examineip.com: 200
    this-domain-does-not-exist.example: NO RESPONSE

This is a loop. while read -r site takes one line at a time from the file named after done <, runs everything between do and done, and repeats until the file runs out. Inside, curl throws away the page (-o /dev/null), gives up after 10 seconds (-m 10) and prints only the status code (-w). If it can’t connect at all, it reports 000.

Try it yourself · part 3

A backup you’ve actually tested

  1. Create the script and paste this in:
    nano backup.sh
    #!/bin/bash
    # Back up the practice folder into ~/backups, with today's date in the name
    mkdir -p ~/backups
    file=~/backups/practice-$(date +%F).tar.gz
    if tar -czf "$file" -C ~ practice; then
      echo "Saved $file"
      ls -lh "$file"
    else
      echo "BACKUP FAILED"
      exit 1
    fi
  2. Make it runnable, and run it:
    chmod +x backup.sh
    ./backup.sh
    Saved /home/sam/backups/practice-2026-11-01.tar.gz
    -rw-r--r-- 1 sam sam 476 Nov  1 09:35 /home/sam/backups/practice-2026-11-01.tar.gz
    tar packs a folder into one compressed file: -c creates, -z compresses and -f names the file. -C ~ runs it from your home folder, and date +%F gives you today’s date in the form 2026-11-01. Putting tar straight after if uses its exit status, so the script can only say “Saved” if the backup really worked. Without that check, it would print “Saved” even when the backup failed.
  3. A backup you’ve never restored is only a hope. Unpack it somewhere safe, and check that your files came back:
    mkdir -p ~/restore
    tar -xzf ~/backups/practice-$(date +%F).tar.gz -C ~/restore
    ls ~/restore/practice
    You should see access.log, notes and the rest. Delete the test with rm -r ~/restore.

One real limitation: this backup sits on the same disk as the originals, so it protects you from your own mistakes but not from a dead disk. For files that matter, copy it to another device as well.

When a script won’t run

The errorWhat it usually means
Permission deniedYou haven’t run chmod +x on it
command not found (for your script)You left off the ./
command not found (for a variable)Spaces around the =
/bin/bash^M: bad interpreterThe file was saved on Windows, with Windows line endings. Fix it with sed -i 's/\r$//' hello.sh, and edit scripts in nano from now on
syntax error near unexpected tokenOften a missing fi, done or then, on or above the line it names

Running it on a schedule

The last step in automating a job is not having to start it yourself. On a machine that stays on, like a server or a Raspberry Pi, cron runs commands at set times. Run crontab -e and add a line like this, which means “at 09:00 every day”:

0 9 * * * /home/sam/scripts/backup.sh

Use the full path, since cron doesn’t start in your folder. A laptop that’s asleep at 09:00 will miss that run, and in WSL, jobs only run while Linux is running, so use this where the machine is actually on.

Key takeaways

  • A script is a text file of commands: a #!/bin/bash line, then chmod +x, then run it with ./.
  • No spaces around =, and double quotes around your variables.
  • if makes decisions and while read repeats for each line of a file.
  • An exit status of 0 means success, and anything else means failure.
  • Test your backups by restoring them, and keep a copy on another device for files that matter.

Check yourself

Three questions. Pick one answer each and you’ll see why.

1. Your script contains name = "Sam" and fails with “name: command not found”. Why?

2. city=Paris, then echo 'I live in $city'. What’s printed?

3. Your backup script runs with no errors, every day. What’s the one thing still left to do?

That’s the course. You can now move around a Linux system, handle files without accidents, pull answers out of logs with pipes, read and set permissions, use sudo carefully and spot a dangerous paste, inspect your network and open ports, and write scripts that check your sites and back up your files. If you haven’t yet, try the three questions on the course page again and see how they feel now.

More free courses are on the Learn page.

This lesson is part of the free course Linux and the Command Line, licensed under CC BY-NC-SA 4.0: you may share and adapt it for non-commercial use, with credit to ExamineIP and a link to the original.

Scroll to Top