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
ifand 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
- Make a folder for your scripts and open a new file in nano:
mkdir -p ~/scriptscd ~/scriptsnano hello.sh - 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)." - 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 SamUsage: ./hello.sh yourname Hello, Sam. Today is Sunday.
What each part does
| Line | What it does |
|---|---|
#!/bin/bash | The 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 1 | Stops 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 |
./? 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=Parisecho "I live in $city"echo 'I live in $city'I live in Paris
I live in $cityPut 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
- 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 - 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 - Make it runnable, and run it:
chmod +x check-sites.sh./check-sites.shexamineip.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
- 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 - Make it runnable, and run it:
chmod +x backup.sh./backup.shSaved /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.gztarpacks a folder into one compressed file:-ccreates,-zcompresses and-fnames the file.-C ~runs it from your home folder, anddate +%Fgives you today’s date in the form 2026-11-01. Puttingtarstraight afterifuses 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. - A backup you’ve never restored is only a hope. Unpack it somewhere safe, and check that your files came back:
mkdir -p ~/restoretar -xzf ~/backups/practice-$(date +%F).tar.gz -C ~/restoreYou should seels ~/restore/practiceaccess.log,notesand the rest. Delete the test withrm -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 error | What it usually means |
|---|---|
| Permission denied | You 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 interpreter | The 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 token | Often 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.shUse 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/bashline, thenchmod +x, then run it with./. - No spaces around
=, and double quotes around your variables. ifmakes decisions andwhile readrepeats 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?
name as a command to run, with the rest as its arguments. Without spaces, it’s a variable being set.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.