Homelab Academy / Modules
Guided practice + break/fix + lab gate.
Module: Linux Foundations
Activity type: Lab (Practice)
Lab risk: low
Objective: Identify file descriptors 0, 1, and 2 as standard input, standard output, and standard error
Build and inspect a service-report pipeline
Every file created, replaced, or appended by this lab must be under /opt/lab-classroom/class17/.
Create the laboratory directory with: sudo install -d -o "$(id -un)" -g "$(id -gn)" -m 0750 /opt/lab-classroom/class17
Enter it with: cd /opt/lab-classroom/class17
Create the source data with: printf 'dns,active,53\nweb,active,443\nbackup,inactive,0\nmetrics,active,9100\nweb,active,80\n' > services.csv
Inspect the source with: cat services.csv
1
Redirect a file into standard input and save the resulting count.
wc -l < /opt/lab-classroom/class17/services.csv > /opt/lab-classroom/class17/service-line-count.txt
2
Use a pipeline to extract active service names, sort them, and count duplicates.
awk -F, '$2 == "active" { print $1 }' /opt/lab-classroom/class17/services.csv | LC_ALL=C sort | uniq -c > /opt/lab-classroom/class17/active-name-counts.txt
3
Use tee to save active service records while also passing them to wc.
awk -F, '$2 == "active" { print }' /opt/lab-classroom/class17/services.csv | tee /opt/lab-classroom/class17/active-services.csv | wc -l > /opt/lab-classroom/class17/active-count.txt
4
Create a log and append a second event without replacing the first.
printf 'pipeline-started\n' > /opt/lab-classroom/class17/events.log; printf 'pipeline-finished\n' >> /opt/lab-classroom/class17/events.log
5
Capture a deliberate read error separately from standard output and record the failing exit status.
cat /opt/lab-classroom/class17/does-not-exist.txt > /opt/lab-classroom/class17/read-output.log 2> /opt/lab-classroom/class17/read-error.log || printf 'cat_exit=%s\n' "$?" > /opt/lab-classroom/class17/read-status.txt
6
Generate stdout and stderr in one command group, then save them in separate files.
{ printf 'normal-message\n'; printf 'diagnostic-message\n' >&2; } > /opt/lab-classroom/class17/stdout-only.log 2> /opt/lab-classroom/class17/stderr-only.log
7
Generate both streams again and combine them using left-to-right redirection.
{ printf 'stdout-message\n'; printf 'stderr-message\n' >&2; } > /opt/lab-classroom/class17/combined.log 2>&1
8
Produce a count grouped by service state.
awk -F, '{ print $2 }' /opt/lab-classroom/class17/services.csv | LC_ALL=C sort | uniq -c > /opt/lab-classroom/class17/state-counts.txt
9
Inspect all generated regular files without modifying them.
find /opt/lab-classroom/class17 -maxdepth 1 -type f -printf '%f\n' | LC_ALL=C sort
| Symptom | Likely cause | Fix |
|---|---|---|
| Creating the laboratory directory reports permission denied. | The current account cannot create directories beneath /opt without elevation. | Run only the provided sudo install command for /opt/lab-classroom/class17, then confirm that the resulting directory is owned by the current user. |
| The awk command produces no active records. | The field separator was omitted, the input file was mistyped, or shell quoting changed the awk program. | Use -F, exactly as shown, keep the awk program inside single quotes, and verify the contents of services.csv with cat. |
| active-name-counts.txt contains unexpected duplicate lines. | uniq received unsorted input because the sort stage was omitted or placed after uniq. | Use the sequence producer |
| An error message appears on the terminal even though stdout was redirected. | Only file descriptor 1 was redirected; file descriptor 2 still points to the terminal. | Add a separate 2> destination or combine the streams with > destination 2>&1. |
| A combined-output command still leaves diagnostics on the terminal. | The redirections were written in the reverse order, causing stderr to retain the shell's previous stdout destination. | Place the stdout redirection first and then duplicate it with 2>&1. |
| A pipeline appears successful even though an early command failed. | The shell returned the exit status of the final pipeline command. | In Bash scripts where every stage matters, enable pipefail with set -o pipefail and test the resulting status. |
| A verification command prints nothing. | The condition failed, a generated file is missing, or the shell did not reach the success-side printf after &&. | Run the tests on each file separately, inspect the file with cat, and compare its content with the expected results. |
| A file unexpectedly lost its previous contents. | The > operator truncated the file before writing. | Use >> only when appending is intended, enable noclobber during interactive work when appropriate, and verify the destination path before pressing Enter. |
Treat every redirection destination as a write operation and verify its complete path before execution.
Keep untrusted data out of shell code. Pipe data as data rather than constructing commands from it.
Do not record credentials, tokens, private keys, or other secrets with tee or diagnostic redirections.
Quote variable expansions used as filenames so whitespace and wildcard characters are not reinterpreted by the shell.
Be cautious when elevated commands write through paths controlled by another user because symbolic links can redirect writes to unintended destinations.
Remember that the shell opens redirection targets before launching the command; privilege boundaries therefore depend on which process performs the open operation.
Use restrictive permissions for logs that could contain hostnames, usernames, internal addresses, or command diagnostics.
Use pipefail in Bash automation when the success of every pipeline stage is required.
All persistent writes in this lesson are confined to /opt/lab-classroom/class17/.
Review the files before removal with: find /opt/lab-classroom/class17 -maxdepth 1 -type f -printf '%f\n' | LC_ALL=C sort
Run: find /opt/lab-classroom/class17 -maxdepth 1 -type f -delete
After confirming the directory is empty, run: rmdir /opt/lab-classroom/class17
Run: test ! -e /opt/lab-classroom/class17 && printf 'Rollback complete\n'