Build a Linux Operations Toolkit

Build Bash scripts to report, back up, and monitor an Ubuntu VM.

Introduction

30 Second Summary

Repeating the same computer checks by hand gets tedious fast. A useful toolkit turns those chores into repeatable commands.

In this project, you will build a command-line operations toolkit inside an isolated Ubuntu 24.04 LTS virtual machine created with Multipass. The finished toolkit gives you reusable Bash scripts for everyday operations work.

What You'll Build

Your terminal becomes a small operations console where each command leaves behind evidence you can inspect.

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

  • Use a system report to inspect a saved snapshot in reports/system-report.txt.
  • Use a compressed backup to package your data workspace into an archive you can inspect.
  • Use a process checker to record timestamped results in logs/process-watch.log.
  • Secret Mission: Add ops_menu.sh so one interactive menu launches all three tools.

Are there any prerequisites?

You'll need a Mac running macOS 14 Sonoma or later with administrator access. Set aside at least 5 GB of free disk space plus 1 GB of memory on an internet-connected Mac.

Before We Start

Before the hands-on work begins, this is your moment to commit to the toolkit you are building. You will connect it to the repetitive Linux tasks you want to replace.

Set Up Your Ubuntu Lab

Your toolkit needs real Linux behavior for permissions, packages, processes, and filesystem commands. A disposable Ubuntu virtual machine keeps that practice separate from your Mac.

Multipass creates a cloud-style virtual machine that behaves like a separate computer. You will confirm your Mac can support the lab before creating it.

In this step, get ready to:
  • Confirm your macOS version, administrator access, storage, memory, and internet access.
  • Install and verify Multipass 1.16.4.
  • Launch an Ubuntu 24.04 LTS VM and enter its Bash shell.
Check your Mac and install Multipass

Multipass requires macOS 14 Sonoma or later. The lab also needs about 5 GB of disk space and 1 GB of available memory.

  • Click the Apple menu in the top-left corner.
  • Select About This Mac.
  • Check the displayed macOS name and version.

✔️ I see Sonoma or later

Good. Your macOS version supports the current Multipass release.

ⓧ I see an older version

Your Mac needs a supported macOS release before Multipass can run.

  • Click the Apple menu.
  • Select System Settings.
  • Select General in the sidebar.
  • Select Software Update.
  • Install an available update to macOS 14 Sonoma or later.
  • Return to About This Mac after the update.
  • Confirm the displayed version is macOS 14 or later.
  • Return to System Settings.
  • Select General in the sidebar.
  • Select Storage.
  • Confirm the storage chart shows at least 5 GB available.

Why check local resources?

A default Multipass instance uses about 1 GB of memory and about 5 GB of disk space. Checking first prevents the VM from competing with your Mac for resources.

  • Press Cmd+Space to open Spotlight.
  • Type Activity Monitor and press Enter.
  • Click Memory at the top of Activity Monitor.
  • Confirm at least 1 GB remains between Physical Memory and Memory Used.

Your Mac now has the local capacity for the lab. One final check confirms that you can authorize the installer.

  • Switch back to System Settings.
  • Select Users & Groups in the sidebar.
  • Click the Info button beside your user account.
  • Confirm Allow this user to administer this computer is enabled.
  • Open the official Multipass installation guide in your browser.
  • Confirm the guide loads to verify your internet connection.

Before installing anything, check whether the required Multipass version is already present. This avoids replacing a working installation unnecessarily.

  • Press Cmd+Space to open Spotlight.
  • Type Terminal and press Enter.
  • Check the installed Multipass version by running this command:
multipass version

What does this command do?

The command asks the Multipass installation to print its version. The result determines whether you can continue or need the installer.

✔️ I see the required version

Your terminal reports Multipass 1.16.4. Your installation is ready to create the lab.

ⓧ I see an older version

Your existing installation needs an update to Multipass 1.16.4. The installer asks for administrator approval as part of the update.

  • Return to the official installation guide from earlier.
  • Follow its macOS download link for multipass-1.16.4+mac-Darwin.pkg.
  • Locate multipass-1.16.4+mac-Darwin.pkg in Finder.
  • Double-click multipass-1.16.4+mac-Darwin.pkg.
  • Complete the guided installer with your administrator account.
  • Return to Terminal after the installation finishes.
  • Repeat the version check shown above.

You should now see Multipass 1.16.4 in the output.

ⓧ Command not found

Multipass is not installed yet. The installer asks for administrator approval while adding the application to your Mac.

  • Return to the official installation guide from earlier.
  • Follow its macOS download link for multipass-1.16.4+mac-Darwin.pkg.
  • Locate multipass-1.16.4+mac-Darwin.pkg in Finder.
  • Double-click multipass-1.16.4+mac-Darwin.pkg.
  • Complete the guided installer with your administrator account.
  • Return to Terminal after the installation finishes.
  • Repeat the version check shown above.

You should now see Multipass 1.16.4 in the output.

Multipass version still unavailable?

Confirm the guided installer finished before returning to Terminal. Reopen Terminal if your existing session still cannot find Multipass.

If the version differs, download the exact multipass-1.16.4+mac-Darwin.pkg asset from the official guide.

Ask for help with the installation result.

Create the Ubuntu lab VM

Multipass uses image aliases to select an operating system release. The noble alias identifies Ubuntu 24.04 LTS.

  • Check the available Multipass images by running this command:
multipass find

What does this command do?

The command lists the operating system images Multipass can launch. You use the result to confirm that noble maps to the intended Ubuntu release.

You should see noble identify Ubuntu 24.04 LTS in the image list.

The first VM launch downloads the Ubuntu image, so the terminal may show progress for a while. That activity means Multipass is building your isolated lab.

  • Create the VM named linux-ops-vm by running this command:
multipass launch noble --name linux-ops-vm

What does this command do?

  • The noble argument selects the Ubuntu 24.04 LTS image.
  • The --name option assigns the reusable instance name linux-ops-vm.
  • Multipass starts the new instance after creating it.

The command completes after the Ubuntu image is downloaded and the named VM is running. Your Linux lab now exists on your Mac.

VM launch did not complete?

Confirm your internet connection is still active. Check that your Mac still has at least 5 GB of storage available.

Ask for help with the launch output.

Enter Linux and verify the environment

The VM is running as a separate Ubuntu system. Entering its shell gives you the Linux command line where every toolkit file and package stays isolated.

  • Enter the VM shell by running this command:
multipass shell linux-ops-vm

What does this command do?

The command connects Terminal to the Bash shell inside linux-ops-vm. It also starts the instance automatically when you reconnect after stopping it.

Your prompt should now identify the Ubuntu VM. Commands entered in this shell run inside the isolated lab.

Before you verify the environment, what operating system do you expect the kernel output to identify?

  • Verify the kernel and Bash version by running these commands:
uname -a
bash --version

What do these commands show?

  • The first command prints the kernel name plus system details from the VM.
  • The second command prints the Bash release installed by Ubuntu.

You should see the first line begin with Linux. The Bash output should include version 5.2.21.

Output does not identify Linux?

Check whether your prompt changed after entering the VM. If it did not change, repeat the shell command from macOS Terminal.

Ask for help identifying the active shell.

✔️ My Ubuntu lab is ready

That is the lab foundation complete. Your named Ubuntu VM is running with a verified Linux kernel and Bash shell.

ⓧ I'd like to double check the lab

Your Multipass instance should be created from the noble Ubuntu 24.04 LTS image.

  • Confirm the VM is named linux-ops-vm.
  • Confirm your open Terminal prompt identifies the VM shell.
  • Confirm the kernel output begins with Linux.
  • Confirm the Bash output includes 5.2.21.
  • Keep this VM shell open for Steps 2 through 5.

Your Ubuntu lab is running without changing the files or settings on your Mac. Next, you will build the toolkit workspace and turn system checks into your first Bash script.

Build Your System Report

Your Ubuntu lab is live. It gives you a safe place to practice real Linux behavior.

Manual system checks disappear into terminal history. A system report writes kernel, disk, and memory details to a file you can inspect again.

You will build a structured workspace for the toolkit. Then you will compare Bash invocation with direct execution.

In this step, get ready to:
  • Create a structured workspace for the operations toolkit.
  • Write a script that saves a system snapshot.
  • Compare two ways of launching the report.
Build the toolkit workspace

The toolkit uses separate directories for scripts, input data, reports, backups, and logs. This structure gives each generated artifact a predictable home.

  • Create all five toolkit directories in the VM shell by running this command:
mkdir -p ~/linux-ops/{bin,data,reports,backups,logs}

What does this command do?

The shell expands the names inside the braces into five directory paths. The -p option creates the parent directory when needed.

  • Move into the new ~/linux-ops directory by running this command:
cd ~/linux-ops

Where are you now?

Your shell now uses ~/linux-ops as its current directory. Commands with relative paths now start from this toolkit workspace.

  • Confirm your current location by running this command:
pwd

What should you see?

The printed path should end in /linux-ops. This confirms that later file paths point into the toolkit.

  • List the workspace directories by running this command:
ls

Check the workspace structure

You should see backups, bin, data, logs, and reports in the output. Your toolkit now has a place for every artifact it creates.

Create the system report

A here-document sends several lines into a command as one input stream. Quoting its delimiter keeps the script text literal while Bash creates the file.

The closing delimiter is easy to mistype. It must sit on a line by itself with no spaces before or after it.

  • Start the quoted here-document by running this command:
cat > bin/system_report.sh <<'EOF'

What just happened?

The shell is now collecting lines for bin/system_report.sh. It keeps the script's variables untouched while it waits for the closing delimiter.

  • Paste the script into the waiting shell by copying this code:
#!/bin/bash

report_dir="$HOME/linux-ops/reports"
report_file="$report_dir/system-report.txt"

mkdir -p "$report_dir"

{
  echo "Linux System Report"
  echo "Generated: $(date)"
  echo
  echo "Kernel and architecture"
  uname -a
  echo
  echo "Disk usage"
  df -h /
  echo
  echo "Memory usage"
  free -m
} > "$report_file"

cat "$report_file"

What does this script do?

  • The report_dir variable identifies the reports directory inside the toolkit.
  • The report_file variable identifies the persistent system report.
  • The grouped commands collect a timestamp plus kernel, disk, and memory details.
  • The final cat command displays the saved report in the terminal.
  • Finish the here-document by entering this delimiter on its own line:
EOF

The script is saved

The closing delimiter ends the input stream. Your shell prompt returns after cat writes the completed script to bin/system_report.sh.

✔️ Awesome, I've got everything!

Your shell prompt has returned. The system report script is ready to run.

ⓧ I'd like to double check the full code

Compare ~/linux-ops/bin/system_report.sh with this complete file.

#!/bin/bash

report_dir="$HOME/linux-ops/reports"
report_file="$report_dir/system-report.txt"

mkdir -p "$report_dir"

{
  echo "Linux System Report"
  echo "Generated: $(date)"
  echo
  echo "Kernel and architecture"
  uname -a
  echo
  echo "Disk usage"
  df -h /
  echo
  echo "Memory usage"
  free -m
} > "$report_file"

cat "$report_file"
Run the script two ways

Bash can read a script file as input. Direct execution asks the operating system to launch the file itself.

  • Run the system report through Bash by running this command:
bash bin/system_report.sh

What should you see?

You should see a timestamped Linux System Report with kernel details. Disk and memory sections appear below it.

The script also writes the same output to reports/system-report.txt. The terminal output and saved report come from one system snapshot.

That is your first automation win. One command now collects several system checks and keeps the result for later.

  • Display the persistent report by running this command:
cat reports/system-report.txt

What does the saved file prove?

You should see the same report structure again. This confirms that output redirection created a persistent file inside reports.

Before you try the second launch method, do you think the shell can execute the script directly?

  • Test direct execution by running this command:
./bin/system_report.sh

This failure is intentional

You should see the shell report Permission denied. The file contains valid Bash code, but direct launch also requires execute permission.

The earlier launch worked because Bash read the file as input. This contrast gives you concrete evidence for the permission investigation ahead.

🙋‍♀️ Seeing a different result?

  • Return to the location check from the first substep if the shell reports a missing file.

If direct execution prints the report instead of the expected permission error, ask for help comparing your file state before continuing.

  • Keep the VM shell open for the next toolkit step.

Your report works when Bash reads it. Next, you will investigate the permission evidence behind the blocked direct launch.

Repair Permissions and Inspect the Workspace

Your report script works when Bash reads it. Direct execution failed because Linux permissions blocked the launch.

File permissions decide which users can read, modify, or execute a file. You will inspect the existing mode before applying the smallest change.

A compact directory view will help you inspect the growing toolkit. You will test whether the clean Ubuntu VM can produce that view.

In this step, get ready to:
  • Identify the missing owner execute permission on bin/system_report.sh.
  • Grant owner execute permission to bin/system_report.sh.
  • Inspect the workspace with a two-level directory view.
Inspect the permission evidence

A long file listing exposes the permission bits that control direct execution. The owner section currently lacks the bit that permits the script to launch.

  • Inspect the current permissions on bin/system_report.sh by running this command:
ls -l bin/system_report.sh

How to read the permission listing

  • The ls -l command displays file permissions beside the filename.
  • The first permission triplet belongs to the file owner.
  • A missing x in that triplet explains why direct execution failed.
  • Open the permission command's manual page by running:
man chmod

What does this command do?

The man command opens the installed documentation for a command. The chmod page explains how symbolic permission changes work.

  • Read the manual's explanation of u+x.
  • Press q to leave the manual.

The symbol u+x adds execute permission for the file owner. It leaves every other permission unchanged.

Grant owner execute permission

The script contents already work. A targeted permission change makes the same file directly executable.

  • Add owner execute permission by running this command:
chmod u+x bin/system_report.sh

What does this command change?

  • The u targets the file owner.
  • The +x adds execute permission.
  • The script's source code stays unchanged.
  • Inspect the updated permissions by running:
ls -l bin/system_report.sh

What should change?

The owner's permission triplet now includes x. That bit authorizes the direct launch you are about to test.

The permission listing now shows that the owner can execute bin/system_report.sh.

Before you run the script directly, do you think the same report will appear this time?

  • Test the repaired permission by running the script directly:
./bin/system_report.sh

Why does direct execution work now?

  • The #!/bin/bash line selects the interpreter for the script.
  • The execute bit now permits Linux to launch that interpreter.
  • The script regenerates reports/system-report.txt before displaying it.

You'll see Linux System Report followed by a fresh Generated: timestamp. The direct launch has regenerated the saved report.

That permission barrier is cleared: your system report now launches directly from the toolkit.

Still blocked from direct execution?

  • Confirm that ls -l shows x in the owner's permission triplet.
  • Confirm that your current directory is still ~/linux-ops.

Ask for help with the permission evidence you see: help me diagnose why system_report.sh still cannot run directly.

The permission change affected file metadata. Your script content should still match the original report logic.

✔️ Awesome, I've got everything!

Your script remains unchanged. It now runs directly because its owner has execute permission.

ⓧ I'd like to double check the full code

Compare bin/system_report.sh with this complete reference.

#!/bin/bash

report_dir="$HOME/linux-ops/reports"
report_file="$report_dir/system-report.txt"

mkdir -p "$report_dir"

{
  echo "Linux System Report"
  echo "Generated: $(date)"
  echo
  echo "Kernel and architecture"
  uname -a
  echo
  echo "Disk usage"
  df -h /
  echo
  echo "Memory usage"
  free -m
} > "$report_file"

cat "$report_file"

Why is the code unchanged?

The execute permission lives in file metadata. The script's variables, commands, and report output remain exactly the same.

Test the workspace viewer

A directory tree turns nested folders into an indented view. Limiting the depth keeps the toolkit overview compact.

Before you run the workspace viewer, do you expect the clean VM to already provide it?

  • Request a two-level view of the current workspace by running:
tree -L 2 .

What does this command request?

  • The -L 2 option limits the view to two directory levels.
  • The . targets your current ~/linux-ops directory.

The shell reports that tree is unavailable. This is the intended obstacle in the clean VM.

This failure is intentional

The unavailable command proves that the workspace exists without its preferred viewer. The VM remains unchanged at this checkpoint.

Your report now runs directly. Next, you'll resolve the missing workspace viewer before automating compressed backups.

Install Tree and Automate Backups

Your system report now runs directly. The missing tree command still leaves your toolkit layout hard to inspect.

APT closes that package gap. A parameterized backup script then turns your data files into a repeatable compressed archive.

In this step, get ready to:
  • Install the pinned tree package with APT.
  • Prepare the data files for your backup workflow.
  • Validate the backup script before inspecting its archive.
Install and audit the missing package

APT is Ubuntu's package manager. It refreshes package information before downloading the exact tree build required by your lab.

Your earlier workspace check established that tree is unavailable. This version check confirms which installation path applies.

Before you run this, which result do you expect: a version or an unavailable command?

  • Check the current tree state by running this command:
tree --version

What does this check do?

The --version option asks tree to print its installed version. Your VM cannot complete that request until the package exists.

Your VM should still report that tree is unavailable. Choose the matching tab below.

✔️ I see the required tree version

A tree binary already exists. Align the Ubuntu package with the pinned build used by this project.

  • Install the exact package build by running these package-management commands:
  • Approve the displayed installation plan when APT pauses.
sudo apt update
sudo apt install tree=2.1.1-2ubuntu3.24.04.2

What do these commands do?

  • The first command refreshes Ubuntu's available package information.
  • The second command selects the exact tree package build for this lab.

APT should finish with tree version 2.1.1-2ubuntu3.24.04.2 configured.

ⓧ I see an older tree version

An older tree binary does not match this lab's pinned package. APT can install the required build in its place.

  • Install the exact package build by running these package-management commands:
  • Approve the displayed installation plan when APT pauses.
sudo apt update
sudo apt install tree=2.1.1-2ubuntu3.24.04.2

What do these commands do?

  • The first command refreshes Ubuntu's available package information.
  • The second command replaces the older package with the pinned tree build.

APT should finish with tree version 2.1.1-2ubuntu3.24.04.2 configured.

ⓧ tree is unavailable

This matches the result from your earlier workspace check. APT now supplies the missing command.

  • Install the exact package build by running these package-management commands:
  • Approve the displayed installation plan when APT pauses.
sudo apt update
sudo apt install tree=2.1.1-2ubuntu3.24.04.2

What do these commands do?

  • The first command refreshes Ubuntu's available package information.
  • The second command downloads the pinned tree package.

The missing command is now installed. APT should finish with version 2.1.1-2ubuntu3.24.04.2 configured.

Package installation not completing?

  • Confirm that the VM still has internet access.
  • Repeat the package refresh command from your selected tab.

Ask for help with the exact point where the installation stopped: help me troubleshoot why APT cannot install the pinned tree package in my Ubuntu VM.

Now the earlier missing-command obstacle has a fix. Before you retry it, predict how the workspace output changes.

  • Display the workspace through the installed directory viewer by running:
tree -L 2 .

What does this command show?

The -L 2 option limits the directory view to two levels. This keeps the toolkit structure readable as more files appear.

You should see backups, bin, data, logs, and reports beneath the current directory. The bin branch should contain system_report.sh.

  • Inspect the package activity log by running:
less /var/log/dpkg.log

Why inspect the package log?

The /var/log/dpkg.log file records package activity inside Ubuntu. The less viewer lets you search that evidence without changing the file.

  • Press / to start a search.
  • Type tree as the search term.
  • Press Enter to find the matching package entry.
  • Press q to leave the log viewer.

You should find an entry recording the tree installation. That log gives you an audit trail for the package change.

Create the data and backup script

In Bash, positional parameters let one script work with different source paths. Your backup script accepts a source directory followed by a destination directory.

  • Create the two data files by running these commands:
echo "server=linux-ops-vm" > data/config.txt
echo "status=healthy" > data/status.txt

What do these commands create?

Each > redirection writes one line into its target file. These values give the archive concrete content to protect.

  • Confirm the new file names appear in the workspace by running:
tree -L 2 .

What should the tree prove?

The directory view now includes config.txt plus status.txt beneath data.

You should see both files under the data branch. Your backup source now contains the expected inputs.

A quoted here-document writes several lines into a file from the shell. Quoting EOF keeps the script's variables untouched while Bash creates the file.

  • Create bin/backup_workspace.sh with a quoted here-document by running:
cat > bin/backup_workspace.sh <<'EOF'
#!/bin/bash

if [ "$#" -ne 2 ]; then
  echo "Usage: $0 SOURCE_DIR DEST_DIR"
  exit 1
fi

source_dir="$1"
destination_dir="$2"

if [ ! -d "$source_dir" ]; then
  echo "Source directory does not exist: $source_dir"
  exit 1
fi

mkdir -p "$destination_dir"
archive_file="$destination_dir/linux-ops-backup.tgz"

tar -czf "$archive_file" -C "$source_dir" .

echo "Archive created: $archive_file"
ls -lh "$archive_file"
EOF

What does this script do?

  • The $# check requires exactly two positional parameters.
  • The $0 value places the script name in its usage message.
  • The $1 value becomes source_dir.
  • The $2 value becomes destination_dir.
  • The archive_file variable points to the compressed backup created by tar.
  • Give the file owner execute permission by running:
chmod u+x bin/backup_workspace.sh

What does this permission change do?

The u+x mode adds execute permission for the file owner. This allows a direct launch through ./bin/backup_workspace.sh.

Good progress. Your second toolkit command is now ready for a direct test.

✔️ Awesome, I've got everything!

Your backup script is saved in bin with owner execute permission.

ⓧ I'd like to double check the full code

The complete file at this point is shown below.

#!/bin/bash

if [ "$#" -ne 2 ]; then
  echo "Usage: $0 SOURCE_DIR DEST_DIR"
  exit 1
fi

source_dir="$1"
destination_dir="$2"

if [ ! -d "$source_dir" ]; then
  echo "Source directory does not exist: $source_dir"
  exit 1
fi

mkdir -p "$destination_dir"
archive_file="$destination_dir/linux-ops-backup.tgz"

tar -czf "$archive_file" -C "$source_dir" .

echo "Archive created: $archive_file"
ls -lh "$archive_file"
Validate the script and inspect the archive

An exit status tells the shell whether a command succeeded. Your script returns 1 when required arguments are missing.

Before you run the script without arguments, do you expect it to create an archive or stop with guidance?

  • Test the missing-argument path by running these commands:
./bin/backup_workspace.sh
echo $?

Why does this failure matter?

  • The argument check stops the script before it uses an incomplete path.
  • The usage message shows the required source and destination order.
  • The status 1 gives other commands a machine-readable failure signal.

You should see Usage: ./bin/backup_workspace.sh SOURCE_DIR DEST_DIR followed by status 1. This is an intentional validation failure.

Not seeing status 1?

  • Confirm that you launched the script without either directory argument.
  • Compare the argument check with the full-code tab above.

Ask for help with the observed output: help me debug why my backup script does not return status 1 without arguments.

  • Create the compressed backup with the required source and destination arguments by running:
./bin/backup_workspace.sh "$HOME/linux-ops/data" "$HOME/linux-ops/backups"

How does this invocation work?

The first argument points to the two data files. The second argument tells the script to place linux-ops-backup.tgz inside backups.

You should see a message beginning with Archive created: followed by a file-size listing. The script has produced backups/linux-ops-backup.tgz.

Before you inspect the archive, which two files do you expect its listing to contain?

  • Verify the archive contents by running:
tar -tzf backups/linux-ops-backup.tgz

What does this verification prove?

The command lists the gzip-compressed archive without extracting it. This checks the stored contents instead of trusting only the creation message.

You should see ./config.txt plus ./status.txt in the archive listing. That proves both data files reached the backup.

Your toolkit can now inspect its own layout and preserve its data in a verified archive. Next, you will add process monitoring with recorded results.

Watch Processes and Record Results

Your report script captures system state. Your backup script preserves the workspace as a compressed archive.

A process checker connects a background job to exact-name matching in your Bash shell.

The checker sends each result through a pipeline into a timestamped log. Its exit status gives automation a compact success or failure signal.

In this step, get ready to:
  • Create an owner-executable process watcher that accepts one process name.
  • Record the running state of a controlled sleep process.
  • Record the stopped state of the controlled process.
Create the process watcher

The watcher accepts one positional argument as an exact process name. It records the result before returning an exit status that another command can inspect.

  • Create bin/process_watch.sh inside ~/linux-ops by running this quoted here-document:
cat > bin/process_watch.sh <<'EOF'
#!/bin/bash

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 PROCESS_NAME"
  exit 1
fi

process_name="$1"
log_file="$HOME/linux-ops/logs/process-watch.log"

if pgrep -x "$process_name" > /dev/null; then
  message="$(date): RUNNING - $process_name"
  echo "$message" | tee -a "$log_file"
  exit 0
else
  message="$(date): NOT RUNNING - $process_name"
  echo "$message" | tee -a "$log_file"
  exit 1
fi
EOF

What does this script do?

  • The "$#" check confirms that the script received exactly one process name.
  • The process_name variable stores the first positional argument.
  • The log_file variable points to the persistent process log.
  • The pgrep -x check looks for an exact process-name match.
  • The tee -a pipeline displays the message before appending it to the log.
  • The exit 0 path reports a match.
  • The exit 1 path reports that no match exists.
  • Give the file owner permission to launch the watcher directly by running:
chmod u+x bin/process_watch.sh

What does this permission change do?

The owner execute bit allows your shell to launch process_watch.sh through its #!/bin/bash interpreter line. Your first process check confirms that the permission works.

✔️ Awesome, I've got everything!

Your watcher is saved in bin/process_watch.sh with owner execute permission.

ⓧ I'd like to double check the full code

Compare your watcher with the complete file below.

#!/bin/bash

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 PROCESS_NAME"
  exit 1
fi

process_name="$1"
log_file="$HOME/linux-ops/logs/process-watch.log"

if pgrep -x "$process_name" > /dev/null; then
  message="$(date): RUNNING - $process_name"
  echo "$message" | tee -a "$log_file"
  exit 0
else
  message="$(date): NOT RUNNING - $process_name"
  echo "$message" | tee -a "$log_file"
  exit 1
fi
Start and detect a controlled process

A background process keeps running while your shell remains available. This gives the watcher a predictable target without interrupting your session.

  • Start the controlled background process by running:
sleep 120 &

What does this command do?

The ampersand starts the controlled delay in the background. Your shell remains available while sleep runs for 120 seconds.

Before you run the check, predict whether exact-name matching finds the controlled process.

  • Check the controlled process before printing the checker's exit status by running:
./bin/process_watch.sh sleep
echo $?

What should you see?

Your checker prints a timestamped line containing RUNNING - sleep. The following 0 confirms that the exact-name check succeeded.

The same timestamped result now exists in logs/process-watch.log because tee -a appended it.

That is the live path confirmed. Your watcher has found the exact process name.

Checker does not report RUNNING?

Start the controlled delay again if 120 seconds have passed. Repeat the permission command above if the script does not launch directly.

Check that the closing EOF was entered on its own line when you created the file.

Help me troubleshoot why the process watcher does not detect sleep.

Stop the process and prove the negative path

A useful checker must distinguish a missing process from a running one. The negative path proves that your log reflects a real state change.

  • Stop the controlled process by running this command:
pkill -x sleep

What does this command do?

The pkill -x command uses exact-name matching to terminate the controlled sleep process. This keeps the experiment scoped to the process you started inside the VM.

Before you check again, predict how the message and exit status change after the process stops.

  • Check the stopped process before printing the checker's exit status by running:
./bin/process_watch.sh sleep
echo $?

What should you see now?

Your checker prints a timestamped line containing NOT RUNNING - sleep. The following 1 confirms that the exact-name check found no match.

This non-zero status gives calling scripts a machine-readable failure signal. The terminal message gives you the human-readable evidence.

🙋‍♀️ Still seeing RUNNING?

Run the stop command above once more if another controlled delay remains active. Make sure the process name is exactly sleep when you run the watcher.

Help me troubleshoot why sleep still appears to be running.

Before you read the log, predict the order of the two recorded states.

  • Display the complete process log by running:
cat logs/process-watch.log

What should the log contain?

You should see the earlier RUNNING - sleep entry followed by the later NOT RUNNING - sleep entry. Each line begins with the timestamp captured during its check.

The order proves that your watcher recorded both sides of the troubleshooting loop. The log remains available after the terminal output scrolls away.

That's the full loop working. Your three-script toolkit now records live process state in a persistent log.

Secret mission

Add an Interactive Toolkit Menu

Your toolkit already solves three operations jobs, but each one still needs its own command. Add a single interactive menu that runs reports, backups, and process checks from numbered choices.

Clean Up Your Resources

Clean Up Your Resources

Your local Multipass lab has no ongoing financial cost. Choose whether to keep it running, pause it for later, or delete it to reclaim its local disk space and memory.

Resources you used:

  • The local Multipass VM named linux-ops-vm. The ~/linux-ops workspace sits on its virtual disk alongside the installed tree package.

Keep everything running

No teardown is needed if you are still using the toolkit. Leaving the VM shell keeps the instance running.

  • Leave the VM shell by running:
exit

What does exit do?

This ends your current shell session. The linux-ops-vm instance continues using its assigned local resources.

Pause - I'll come back to this later

A stopped VM releases active memory while preserving its virtual disk. Your toolkit is ready when you reconnect.

  • Leave the VM shell by running:
exit

Where does this take you?

This returns you to macOS Terminal. You can now manage the VM from your Mac.

  • Stop linux-ops-vm from macOS Terminal by running:
multipass stop linux-ops-vm

What does stopping preserve?

Multipass stops the VM while preserving its virtual disk. Your scripts, report, backup archive, process log, data files, and installed package remain available.

  • Reconnect to the stopped VM later by running:
multipass shell linux-ops-vm

How does reconnection work?

This command starts the stopped instance automatically. It opens a new shell with your toolkit intact.

Delete - I don't want to use this again

Deletion is permanent. The purge is contained to linux-ops-vm, so files elsewhere on your Mac stay untouched.

  • Leave the VM shell by running:
exit

What happens to the VM?

This closes the shell connection without deleting anything. You return to macOS Terminal for the permanent cleanup command.

  • Permanently delete linux-ops-vm by running:
multipass delete --purge linux-ops-vm

What gets removed?

This permanently removes linux-ops-vm from Multipass.

The ~/linux-ops workspace disappears with the VM. The installed tree package disappears too.

Nice Work!

Nice Work!

You did it! Your Ubuntu lab now runs a reusable Bash operations toolkit.

You've learned how to:

  • Built a structured Linux operations workspace. Diagnosed an execute-permission failure. Installed a pinned package with APT.
  • Created a reusable system report that captures system details in a persistent file. Built a parameterized backup script that produces compressed archives. Inspected each archive to verify its contents.
  • Built a process watcher that writes timestamped log entries. Used exit statuses to distinguish running processes from stopped ones.
  • Completed the optional Secret Mission by adding an interactive toolkit menu that runs the report, backup, or process checker from one place.

Ready to quiz yourself?