Step 28. bash Scripts and cron — Hand Your Typed Commands Over to an Alarm Clock

Step 28. bash Scripts and cron — Hand Your Typed Commands Over to an Alarm Clock

Level 0 — Understanding Computer Operation and Structure | Difficulty ★★☆☆☆ | Estimated time: 3 hours

Prerequisites: You must have finished Steps 18–27 (Linux fundamentals, permissions, processes, directory structure). You need an Ubuntu VM (or WSL Ubuntu).

  • What you need: An Ubuntu terminal and the nano editor. Nothing new to install.
  • Caution: Everything you make today is small text files in your own home folder, so it’s safe. But there’s one iron rule — if you register something in cron, you must unregister it when you’re done. Leave it on and forget it, and the log file will keep growing forever.

Until now, we’ve typed commands one line at a time by hand. But if you had to type "check the disk, check the memory, back up the logs" every morning, you’d be exhausted in three days. Computers were built to take over repetition. A script — a "screenplay" of commands written in a file and executed all at once — and cron, the alarm clock that runs that script at a set time on its own: this combination is the alpha and omega of automation.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Create a shell script that gathers several commands in a file and runs them at once
  • Explain what the first-line #!/bin/bash (shebang) means
  • Use $(command) and variables to produce changing output, like "a different filename every day"
  • Read cron’s five-field time expression (minute hour day month weekday), and register, check, and unregister a schedule
  • Know the two things to check when cron doesn’t run (absolute paths, execute permission)

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language & environment bash shell script + nano — Ubuntu terminal
Today’s commands chmod +x (execute permission), ./script.sh (run), $(command) (insert output), crontab -e / crontab -l (edit/view the schedule), date +%Y-%m-%d (date format)
Concepts needed Shebang, execute permission, variables, cron’s five-field time expression, the difference between the cron environment and the terminal environment

2-1. The Structure of a Shell Script

A shell script is just "a text file with commands written top to bottom." But there are two rules.

  1. First line — the shebang: #!/bin/bash. The path after #! is a marker saying "execute this file with this program." /bin/bash is the very shell we use. With this line, the system understands, "ah, this is a bash script."
  2. Execute permission: As you learned in Step 23, a file needs the x permission to be treated as a program. Forget chmod +x and you’ll meet "Permission denied."

Inside a script, every command you typed in the terminal works as-is. $(command) means "insert this command’s output right here," and a line starting with # (except the first-line shebang) is a memo that doesn’t execute — a comment.

2-2. cron — Linux’s Alarm Clock

cron is a daemon (Step 26) that resides in the background, waking up every minute to check "is there a schedule that should run right now?" and executing it. Each user has their own schedule file, edited with crontab -e (short for cron table).

The format of one schedule line:

* * * * *  command_to_run
minute hour day month weekday

The five fields are the "when." A * means "every":

  • * * * * * → every minute
  • 0 * * * * → every hour on the hour (when the minute is 0)
  • 0 3 * * * → every day at 3 AM
  • */10 * * * * → every 10 minutes

In the verified environment (WSL Ubuntu 24.04), the cron daemon was residing as /usr/sbin/cron (confirmed 2026-09-09 with ps aux | grep cron). The alarm clock that quietly wakes every minute really is alive.

2-3. cron’s Two Traps

cron is a different environment from the terminal where we type commands. A shell a person logs into loads various environment settings, but cron runs the bare command with none of that. Hence the two mistakes every beginner goes through at least once:

  1. Using relative paths: Write cron.log without a path and the "relative to where?" baseline differs, so the file appears somewhere unexpected — or doesn’t appear at all. → Write every path as an absolute path, like /home/username/....
  2. Missing execute permission: Without chmod +x, cron can’t run it either. → Run it by hand and confirm before registering.

Remember just these two and you prevent half of all cron problems.


3. Follow Along

3-1. Writing Your First Script

nano hello.sh

Inside nano, write the following three lines, save (Ctrl+O, Enter), and exit (Ctrl+X):

#!/bin/bash
# My first script — prints the current time with a greeting
echo "Hello! It is now $(date)."
chmod +x hello.sh
./hello.sh
Hello! It is now Wed Sep  9 11:29:10 KST 2026.

(The time portion was verified 2026-09-09 — we confirmed that date‘s output really comes out in this format. Your run time will differ.)

How to read it: The output of the date command got inserted where $(date) was. This is the moment Step 23’s execute permission and Step 19’s use of command results come together. The ./ means "run this file in the current folder."

Why: This three-line file is a "program." You just made and ran a program on Linux.

3-2. Stacking into a File Instead of the Screen

./hello.sh >> hello.log
./hello.sh >> hello.log
cat hello.log
Hello! It is now Wed Sep  9 11:31:20 KST 2026.
Hello! It is now Wed Sep  9 11:31:23 KST 2026.

(Sample output — the time format matches the live capture.)

How to read it: This is the >> (append redirection) from Step 19. Instead of the screen, lines keep stacking at the end of the file. You can see two lines with different times.

Make a prediction: If you had used > instead of >>, how would the cat result differ? (Answer: > overwrites, so only the last line would remain. Automation logs are meant to "accumulate," so we use >>.)

3-3. Scheduling in cron

crontab -e

The first time you run it, you may be asked which editor to use. Choose nano (usually option 1). When a file full of comments opens, add the following line at the very bottom, then save and exit (replace lee with your own username):

* * * * * /home/lee/hello.sh >> /home/lee/cron.log 2>&1
crontab: installing new crontab

(Sample output — the actual message that appears when the save succeeds.)

How to read it: The schedule is registered. The 2>&1 at the end means "send the error output (stream 2) to wherever the normal output (stream 1) is going" — Step 19’s stream knowledge comes back into play here. Error messages must land in the log too, or you won’t be able to trace "why didn’t it run?" later.

By the way, on an account that has never registered a schedule, crontab -l (view the schedule) shows this:

no crontab for root

(Verified 2026-09-09 — the live environment was an admin account, so it showed root; yours will show your username. After registering, the line you entered appears as-is.)

Why: From now on, every minute — without you lifting a finger — hello.sh runs and its results stack into cron.log.

3-4. Confirming the Automatic Run — and Unregistering (Mandatory!)

After waiting 2–3 minutes:

cat /home/lee/cron.log
Hello! It is now Wed Sep  9 11:35:00 KST 2026.
Hello! It is now Wed Sep  9 11:36:00 KST 2026.

(Sample output — the format matches real behavior.)

How to read it: Lines stacked up on the hour while you didn’t move a finger. This is automation.

Now without fail, unregister it:

crontab -e

Delete the line you added earlier, save and exit, then check:

crontab -l

How to read it: -l "views" the schedule. If the line from before is gone, you’re done.

Why: Leave an every-minute schedule running and the log swells forever. The iron rule of automation: if you can turn it on, you must know how to turn it off. Same philosophy as the account cleanup in Step 25.

3-5. Putting Variables in a Script

This is the moment a script starts looking like a real program. The same concept as the PowerShell variables from Step 7 exists in bash:

nano backup-note.sh
#!/bin/bash
# A script that creates a memo file with today's date in its name
TODAY=$(date +%Y-%m-%d)
echo "Today's (${TODAY}) to-do:" > "/home/lee/memo-${TODAY}.txt"
echo "1. Review Linux" >> "/home/lee/memo-${TODAY}.txt"
echo "Created file: memo-${TODAY}.txt"
chmod +x backup-note.sh
./backup-note.sh
cat memo-*.txt
Created file: memo-2026-09-09.txt
Today's (2026-09-09) to-do:
1. Review Linux

(The date portion, 2026-09-09, is exactly the live result of date +%Y-%m-%d. It changes to whatever day you run it.)

How to read it: TODAY=$(date +%Y-%m-%d) means "put the output of the date command into a box called TODAY." After that, writing $TODAY drops the contents into that spot. +%Y-%m-%d is date’s output format specification (year-month-day); we verified live that it produced 2026-09-09. The filename changing every time based on the date — that’s the power of a variable.

Why: Automations like "a backup file with a different name every day" all follow this pattern. Variables are the first step that lifts a script from "recorded playback" to "a program that thinks."


4. Missions & Exercises

Mission — Put Your Own Clock on cron

  1. Create clock.sh so it prints "current time + today’s date" (hint: $(date) and $(date +%Y-%m-%d))
  2. After chmod +x, run it by hand and check the output — verifying by hand before registering in cron is the rule
  3. Register it in cron as */2 * * * * (every even minute) and observe for 5 minutes that output accumulates in /home/username/clock.log (2>&1 at the end is mandatory)
  4. Check the accumulated log with cat, then without fail unregister the schedule and confirm it’s empty with crontab -l
  5. Record today’s whole process (write → run by hand → register → observe → unregister) in five lines in cron-report.txt

Exercises

Question 1. Spell out when each cron expression runs — 30 2 * * *, 0 9 * * 1, */5 * * * *.

Question 2. Why is the first-line #!/bin/bash needed, and what changes if that line is missing?

Question 3. Running ./hello.sh by hand works, but through cron nothing happened at all. What are the first two things to check?

Question 4. In the schedule line’s ending >> /home/lee/cron.log 2>&1, why attach 2>&1? What regret awaits if you leave it off?


5. Model Answers & Completion Criteria

Mission Model Answer

nano clock.sh
#!/bin/bash
# clock.sh — a clock that prints the current time and date
echo "Now: $(date) / Today: $(date +%Y-%m-%d)"
chmod +x clock.sh
./clock.sh          # check by hand first — that's the rule
crontab -e          # add the line below at the very bottom
*/2 * * * * /home/lee/clock.sh >> /home/lee/clock.log 2>&1
# 5 minutes later
cat /home/lee/clock.log
crontab -e          # delete the line, then save
crontab -l          # final check that it's empty

How to verify: ① Did the hand run show both time and date? ② Did lines accumulate in the log at 2-minute intervals (the minute part of the time jumping by two, landing on even numbers)? That interval is proof you wrote the cron expression correctly. ③ After unregistering, is the line absent from crontab -l? ④ Does the report in step 5 include "I unregistered it too"? The one who checks unregistration before registration is a real operator.

Exercise Solutions

Question 1 solution. 30 2 * * * is every day at 2:30 AM. 0 9 * * 1 is every Monday (weekday 1) at 9 AM. */5 * * * * is every 5 minutes.

Question 2 solution. The shebang is a marker saying "execute this file with this program." It must say /bin/bash for the system to interpret the file as a bash script. Without that line, a different shell may interpret it depending on the mood of the shell that runs it, and bash-specific syntax can break.

Question 3 solution.Paths — are the paths in the schedule line and inside the script all absolute (cron’s working-folder baseline differs)? ② Execute permission — does ls -l show the x permission? The diagnostic order narrows down: "does it work by hand → does it work via cron?"

Question 4 solution. 2>&1 is the device that sends error output to the log file too. Without it, when a scheduled run fails, the error message is left nowhere, and every way of tracing "why didn’t it work" disappears. In automation, logs aren’t decoration — they’re the only witness.

Completion Criteria Checklist

  • [ ] I can explain what the shebang (#!/bin/bash) means
  • [ ] I can run a script with ./script.sh after chmod +x
  • [ ] I can produce changing output with $(command) and variables
  • [ ] I can read cron’s five-field time expression (minute hour day month weekday)
  • [ ] I can register in cron, check with crontab -l, and unregister
  • [ ] I can state the two things to check when cron doesn’t run (absolute paths, execute permission)
  • [ ] Mission: I completed clock.sh’s automatic run and unregistration, plus cron-report.txt

6. Common Pitfalls & Fixes

Wall 1. "I definitely registered it, but cron.log never appears"

Symptom: You wait several minutes and the log file itself doesn’t exist.
Cause: The most common cause is paths. If you use a relative path like cron.log in the schedule line, cron’s working-folder baseline differs, so the file is created elsewhere or writing fails. The second cause is a missing execute permission on the script.
Fix: Change all paths to absolute ones like /home/username/..., and check the x permission with ls -l. And first run ./hello.sh by hand to see whether it works. Narrow it down in the order "works by hand → works via cron."

Wall 2. "It works by hand, but not through cron"

Symptom: Direct execution is perfect; only the scheduled run fails.
Cause: The environment difference. cron has a minimal PATH (the list of folders where commands are searched for), so it may not find some commands inside your script.
Fix: Writing commands inside the script as absolute paths is the sure way (like /usr/bin/date — check the location with which date; in the 2026-09-09 live capture, date was /usr/bin/date). And use 2>&1 at the end of the schedule line so error messages land in the log — then read them. The error tells you.

Wall 3. "I ran crontab -e and a strange editor opened"

Symptom: Instead of nano, vim (or another editor) opens.
Cause: It follows the system’s default editor setting. For you, having finished Step 22, vim is no longer a terror.
Fix: If vim opens, exit with :q!, then choose nano in the editor selection menu. Or just edit in vim — save with :wq.

Wall 4. "I deleted the schedule but it keeps running"

Symptom: You deleted the line in crontab -e, but the log keeps growing.
Cause: You exited without saving, or it’s also registered in another schedule file (a different account, or the system-wide /etc/crontab).
Fix: Check the current schedule with crontab -l, and verify you saw the "installing new crontab" message after editing. Another method: look at the timestamp of the newest log line to confirm it’s really "still" accumulating — if nothing has stacked since the last line, it already stopped.

Wall 5. "I ran the script and got Permission denied"

Symptom: You typed ./hello.sh and got bash: ./hello.sh: Permission denied.
Cause: No execute permission. You created the file but forgot chmod +x.
Fix: Run chmod +x hello.sh, then run it again. This error is Linux’s way of enforcing permissions itself — and you’ll meet it hundreds more times.


7. Summary

Today’s Concepts

Concept One-line description
Shell script A text file of commands written top to bottom — and that’s a program
Shebang (#!/bin/bash) A first-line marker saying "execute this file with bash"
$(command) Insert that command’s output right there
cron An alarm-clock daemon that wakes every minute and runs schedules
crontab The per-user schedule — minute hour day month weekday command
cron’s two big traps Relative paths · missing execute permission

Today’s Commands

Command What it does
chmod +x file Grant execute permission
./script.sh Run the script in the current folder
>> / 2>&1 Append output to a file / send errors to the same place
date +%Y-%m-%d Date in year-month-day format
crontab -e Edit the schedule
crontab -l View the schedule (for confirming unregistration)

The Instinct That Matters More Than Commands

Today you climbed from "a person who types by hand" to "a person who sets things in motion." Automation isn’t some grand technology; it’s a chemical reaction that happens when small things meet — a three-line script met cron and became "a worker that works while I sleep." And running the full cycle of turning it on, checking it, and turning it off is what completes today.

Remember two more things. First, besides the per-user schedule, there are system-wide folders like /etc/cron.daily — the verified environment held housekeeping scripts like logrotate, man-db, and dpkg (checked 2026-09-09). Cron deserves much of the credit for Ubuntu quietly taking care of itself. For computers that aren’t always on, like laptops, there’s also a complement called anacron that "catches up on missed jobs in a batch." Second, cron is also an attacker’s hiding place — a classic technique is an intruder planting a malicious command in crontab as a "survives reboot" device (persistence — the Linux edition of the persistence concept from Step 15). That’s why every incident investigation checklist includes "check every user’s crontab." The crontab -l you learned today is that investigative tool. The tool is the same; who wields it and how is what differs.

From now on, whenever you meet a repetitive task, recall today’s question: "Could I make this a script? Could I run it with cron?" The answer is almost always "yes."


Once every box is checked, Step 28 is complete. Click the checkbox in the sidebar to save your progress.