Um momento
0x110Lesson 18 of 18

Capstone: the station log report

Combine pipelines, loops, associative arrays, functions and strict mode into a real reporting tool.

45 min 6-question quiz 3 code exercises
By the end of this lesson you can
  • Break a reporting task into small, testable steps
  • Choose between a pipeline and a Bash loop for each step
  • Write a complete, robust script that turns a raw log into a status report

Incoming message from Commander Hopper:

Engineer - every morning I scroll through station.log by hand to work out whether the station is on fire. I would like a report on my screen by 09:30 instead. Make it so.

Each log line has four parts - a time, a level (INFO, WARN or ERROR), a subsystem, and a free-text message:

08:09 ERROR reactor coolant pump stalled

The finished tool will print:

1=== KESTREL LOG REPORT ===
2Entries: 12 (08:00-09:15)
3ERROR: 5  WARN: 3  INFO: 4
4Busiest: reactor (5 entries)
5Latest error: 09:10 reactor: pressure spike
6Status: RED

You’ll build it in three exercises. First a quick pipeline, then a loop with associative arrays, then the full report.

Step 1: answer questions with pipelines

Before writing a big script, find out how far one-liners get you. They’re quick to try and easy to check. Counting lines per level is the frequency-count pattern from the pipelines lesson, with awk reformatting the result and adding a total:

step1.sh
1log='08:00 INFO galley coffee brewed
208:09 ERROR reactor coolant pump stalled
308:12 WARN comms signal weak
408:20 ERROR reactor pressure spike'
5cut -d' ' -f2 <<< "$log" | sort | uniq -c | sort -k1,1nr -k2,2 |
6  awk '{ print $2, $1; total += $1 } END { print "total", total }'
Output
ERROR 2
INFO 1
WARN 1
total 4

A pipeline can continue on the next line after a | - handy for long ones. sort -k1,1nr -k2,2 sorts by count (numerically, descending), then by name, so ties come out in a predictable order.

Step 2: one pass, many tallies

The full report needs several numbers at once: counts per level and per subsystem, the first and last times, and the latest error. Running a separate pipeline for each would read the log many times. Instead, read it once with while read, splitting each line into fields, and keep tallies in associative arrays:

step2.sh
1declare -A by_subsystem
2latest_error="none"
3while read -r time level subsystem message; do
4  (( by_subsystem[$subsystem] += 1 ))
5  [[ $level == ERROR ]] && latest_error="$time $subsystem: $message"
6done <<'EOF'
708:09 ERROR reactor coolant pump stalled
808:12 INFO galley coffee brewed
908:20 ERROR comms antenna misaligned
1008:31 INFO reactor temperature stable
11EOF
12for name in "${!by_subsystem[@]}"; do echo "$name ${by_subsystem[$name]}"; done | sort
13echo "latest error: $latest_error"
Output
comms 1
galley 1
reactor 2
latest error: 08:20 comms: antenna misaligned

read -r time level subsystem message puts the first three words in their own variables and everything else in message, so multi-word messages survive intact.

Step 3: find the busiest subsystem without a pipeline

To pick the subsystem with the most entries, you could pipe the tallies through sort | head -n 1. But under set -o pipefail, head closing the pipe early can occasionally make sort fail with SIGPIPE - and with set -e, that ends your script. A small loop avoids the problem, and breaks ties alphabetically:

step3.sh
1set -euo pipefail
2declare -A tally=([galley]=3 [comms]=5 [reactor]=5)
3busiest=""
4for name in "${!tally[@]}"; do
5  if [[ -z $busiest ]] || (( tally[$name] > tally[$busiest] )) ||
6     { (( tally[$name] == tally[$busiest] )) && [[ $name < $busiest ]]; }; then
7    busiest=$name
8  fi
9done
10echo "Busiest: $busiest (${tally[$busiest]} entries)"
Output
Busiest: comms (5 entries)

Where to go next

You can now automate almost anything you can type. Some ideas for what to learn next:

  • cron and systemd timers - run your report every morning at 09:25.
  • ssh and rsync - run commands and copy files on other machines.
  • tmux - keep sessions running and split your terminal into panes.
  • jq - a sed-and-awk for JSON, the language of most APIs.
  • make - describe how to build things once, and rebuild only what changed.

And whenever a script outgrows a page or two, or needs real data structures, consider moving to Python. Knowing when to switch is part of shell wisdom too.

Key takeaways

  • Start with pipelines to explore; switch to a single while read pass when you need many numbers at once.

  • Associative arrays turn a loop into a set of tallies.

  • Under strict mode, avoid fragile | head pipelines in substitutions and trailing && decisions.

  • Test scripts with small, tricky inputs - including the boring “nothing went wrong” case.

Lesson quiz

6 questions · pass with 5 correct · up to 50 XP

Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.

Practice: write Bash scripts

Write a script in the editor and run it for real against sample input. Each run gets a fresh Linux sandbox with Bash 5.2 and the GNU tools on Wandbox, a free public service - so experiment freely, even with rm. Your script and test input are sent there.

Exercise 1

Level summary

+25 XP

stdin is a station log. Print each level with its count, most frequent first (ties alphabetically), then total and the number of lines:

1ERROR 5
2INFO 4
3WARN 3
4total 12
  • Morning log
  • A tie
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Exercise 2

Error hotspots

+25 XP

stdin is a station log. For each subsystem with at least one ERROR, print its name, its error count and the times of its errors in log order, like reactor 3: 08:09 08:40 09:10. List the subsystem with the most errors first; break ties alphabetically.

  • Morning log
  • An outage
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Exercise 3

The full report

+25 XP

Write the complete report for Commander Hopper. stdin is a station log; print:

1=== KESTREL LOG REPORT ===
2Entries: 12 (08:00-09:15)
3ERROR: 5  WARN: 3  INFO: 4
4Busiest: reactor (5 entries)
5Latest error: 09:10 reactor: pressure spike
6Status: RED
  • Entries: the number of lines, with the first and last times.
  • The level counts always show all three levels, even when a count is 0. (Note the two spaces between them.)
  • Busiest: the subsystem with the most entries of any level; ties go to the alphabetically first name.
  • Latest error: time subsystem: message for the last ERROR line, or none.
  • Status: RED if there are any errors, otherwise AMBER if there are any warnings, otherwise GREEN.

The starter has strict mode switched on - keep it, and make sure your script still exits with status 0.

  • Morning log
  • A calm afternoon
  • Warnings and a tie
  • An outage
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Questions about this lesson

Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.

Loading posts…

Gostou da aula? 😆👍
Apoie nosso trabalho com uma doação: