Lesson 003 · Linux Administration Learning Path

Linux Shell and Command-Line Foundations

· Published · 9 min read

Labelled RHEL Linux administration learning path highlighting shell parsing quoting input output pipelines exit status and safe automation

The shell is not merely a place to type commands. It parses quoting and expansions, connects file descriptors, launches programs and reports status. Many production mistakes happen before the intended command begins: an empty variable broadens a target, a wildcard matches more than expected, a pipeline hides an earlier failure, or output redirection truncates the evidence file that was meant to be protected.

Separate the shell from the command it launches

Bash reads the command line, performs expansions and redirections, then executes a builtin or resolves an external program through PATH. Quotes influence what Bash passes as arguments; they do not change how the called program interprets those arguments. Use printf "%q\n" and arrays when you need to inspect or preserve boundaries.

Interactive convenience is different from automation. Aliases, shell options, working directory, locale, umask and environment may vary. A production script declares its interpreter, validates inputs, uses explicit paths where ownership matters, checks exit status, creates protected temporary files and leaves useful evidence.

Build the working model

LayerQuestion to answerEvidence
ParsingWhat tokens and expansions will Bash produce?Quoted command review and printf %q
ResolutionBuiltin, function, alias or external binary?type -a, command -V and package ownership
Data flowWhere do stdin, stdout and stderr go?Explicit file descriptors and protected output path
StatusWhich command decides success?Exit code, PIPESTATUS and application evidence
Side effectsWhat can be created, replaced or disclosed?Exact targets, permissions, dry run and cleanup

Implementation sequence

  1. Read the operation literally. Identify shell operators, expansions, globs and redirections before considering the utility options.
  2. Resolve the command. Use local help and package ownership to know which implementation will run.
  3. Protect argument boundaries. Quote variables and use arrays when one logical argument may contain whitespace or wildcard characters.
  4. Separate normal output from errors. Send evidence to a protected path and keep stderr visible or deliberately captured.
  5. Check status and result. A zero status can still produce the wrong business result; verify the intended artifact or state.
  6. Make cleanup exact. Temporary paths must be created safely and removed only after their value and identity are confirmed.

Commands and expected evidence

type -a printf
command -V ss
rpm -qf "$(command -v ss)"
help printf
man 1 printf
info coreutils 'printf invocation'
printf '%q\n' "$PATH"
printf '%s\n' alpha beta > output.txt
printf '%s\n' warning >&2
set -o pipefail
printf '%s\n' alpha beta | grep beta
printf 'pipeline_status=%s\n' "$?"
tmp_dir=$(mktemp -d)
printf 'temporary=%s\n' "$tmp_dir"
  • type -a exposes aliases, functions, builtins and every PATH match. This matters when an interactive shell behaves differently from a service.
  • Command substitution removes trailing newlines. Do not use it to preserve arbitrary binary or record-oriented data.
  • set -o pipefail makes a pipeline fail when an earlier stage fails, but the script must still handle the status and partial output.
  • mktemp -d creates an unpredictable directory safely. Set a restrictive umask and use a trap only after confirming the trap cannot expand an empty or broad target.

Evidence and acceptance criteria

EvidenceHealthy resultFailure meaning
Command resolutionExpected binary and owning signed packageAlias, function, PATH drift or unowned executable
ArgumentsEach logical value remains one intended argumentQuoting or expansion changed the target
Output channelsEvidence and errors land in approved protected destinationsTruncation, disclosure or lost diagnostics
Exit statusFailure propagates through functions and pipelinesAutomation can report success after partial failure
ArtifactExpected file/state exists with correct owner and modeCommand success did not satisfy the actual task

Worked scenario: an empty variable turns cleanup into an outage

A cleanup script constructs a release path from an unset environment variable and runs a recursive removal. The shell expands the empty value before the utility sees it, changing a specific release target into a broad parent path. Quoting alone cannot make an empty target correct.

The repaired procedure validates that the release ID matches an allowed pattern, resolves the candidate under an approved base directory, compares it with the active release, prints the exact target, and refuses root, the base directory or any path outside the release tree. A filesystem snapshot or retained immutable artifact provides recovery. The lesson is to validate meaning, not only syntax.

Practical how-to cases

Case 1: Preserve filenames as arguments

Create awkward filenames and process them without word splitting, option confusion or wildcard expansion.

work=$(mktemp -d)
printf '%s' data > "$work/name with spaces"
printf '%s' data > "$work/--help"
find "$work" -maxdepth 1 -type f -print0 | while IFS= read -r -d '' f; do printf '<%q>\n' "$f"; done
CheckpointWhat to establish
Expected resultEach path is printed once as one argument, including spaces and the leading dashes.
If it failsBroken output or unexpected help text indicates unsafe splitting or option parsing.
Safe recoveryOperate inside the verified temporary directory and use -- for utilities that support end-of-options.

Case 2: Handle pipeline failure

Demonstrate why a formatter at the end of a pipeline can hide failure from an earlier command.

set +o pipefail
false | tee /tmp/pipeline.out
printf 'without=%s\n' "$?"
set -o pipefail
false | tee /tmp/pipeline.out
printf 'with=%s stages=%s\n' "$?" "${PIPESTATUS[*]}"
CheckpointWhat to establish
Expected resultThe first pipeline can report zero, while pipefail makes the producer failure visible.
If it failsIf automation still reports success, the status was overwritten by another command before it was stored.
Safe recoveryCapture status immediately and remove only the exact temporary output after inspection.

Case 3: Use redirection deliberately

Keep normal output, diagnostics and a combined transcript separate.

{ printf 'record\n'; printf 'warning\n' >&2; } >out.txt 2>err.txt
{ printf 'record\n'; printf 'warning\n' >&2; } >all.txt 2>&1
printf 'out=%s err=%s all=%s\n' "$(wc -l <out.txt)" "$(wc -l <err.txt)" "$(wc -l <all.txt)"
CheckpointWhat to establish
Expected resultout.txt and err.txt each contain one line; all.txt contains both in execution order.
If it failsAn empty pre-existing file may have been truncated before a failing command ran.
Safe recoveryWrite new evidence to a protected new file, verify it, then rotate the earlier file instead of overwriting it.

Case 4: Write a defensive input check

Accept only a simple release identifier and refuse empty or malformed input before constructing a path.

release=${1-}
if [[ ! $release =~ ^[a-z0-9][a-z0-9._-]{0,31}$ ]]; then
  printf 'invalid release\n' >&2
  exit 64
fi
printf 'candidate=/srv/releases/%q\n' "$release"
CheckpointWhat to establish
Expected resultValid identifiers produce one bounded candidate; empty, slashed and whitespace values exit 64.
If it failsA constructed path containing traversal or an empty suffix means validation happened too late.
Safe recoveryRefuse the action. Do not attempt cleanup until the base path and resolved target are independently checked.

Case 5: Search and archive a directory

Select records with a regular expression, create a bzip2-compressed archive, list it, and extract into an empty target.

grep -En '^(ERROR|WARN)[[:space:]]' app.log
tar -cjf logs.tar.bz2 logs/
tar -tjf logs.tar.bz2 | head
mkdir restored
tar -xjf logs.tar.bz2 -C restored
diff -qr logs restored/logs
CheckpointWhat to establish
Expected resultgrep reports only intended records and the extracted tree compares equal to the source.
If it failsAn archive can contain absolute paths or parent traversal; list untrusted archives before extracting in an isolated directory.
Safe recoveryDelete only the isolated extraction after inspection and retain the original archive until its integrity and ownership are established.

Independent practice tasks

  1. Write a script that accepts three filenames safely and reports missing files without stopping the remaining checks.
  2. Use grep with a basic and an extended regular expression, then explain which program interprets each metacharacter.
  3. Archive a directory with tar, list the archive and extract it into a new empty directory without overwriting the source.
  4. Find the package owner and manual page for five commands used elsewhere in this course.

For this lesson on Linux Shell and Command Line, complete each task without copying the worked command sequence. Record the initial state, exact change, verification, negative test and recovery command. A task is unfinished if it works now but does not survive a reboot where persistence is required.

Troubleshooting by symptom

SymptomInspect firstDefensible next action
Command works interactively but not in a serviceInterpreter, PATH, working directory, environment and permissionsDeclare dependencies and service environment explicitly
Pipeline reports success despite an errorStatus of each stage and whether pipefail is activePreserve the first failure and avoid masking it with a final formatter
Filename beginning with a dash is treated as an optionArgument construction and utility support for --Use -- and an explicit path such as ./name
Output file is empty after a failed commandOrder of redirection and command executionRestore retained evidence; redirecting with > truncates before execution

Unsafe operations and recovery boundaries

  • Unsafe: eval on constructed or untrusted text reparses data as shell syntax. Use arrays and explicit commands instead.
  • Unsafe: unquoted variables and globs in destructive commands can broaden the target. Validate non-empty canonical paths and refuse protected roots.
  • Unsafe: piping a network download directly to a privileged shell removes artifact inspection, digest verification and rollback evidence.

Rewritten knowledge checks

What does quoting protect?
It controls shell expansion and argument boundaries. It does not validate that the resulting value is safe or correct.
How do you discover what <code>name</code> will execute?
Use type -a name or command -V name, then verify the external binary package where relevant.
What is the difference between stdout and stderr?
They are separate file descriptors for normal output and diagnostics; the caller decides where each is connected.
Why can a pipeline hide failure?
Without pipefail, the pipeline status normally reflects the last command even when an earlier producer failed.
What does <code>$@</code> preserve when double quoted?
It expands positional parameters as separate arguments, preserving their boundaries.
Why is <code>cd</code> usually a shell builtin?
It must change the working directory of the current shell; an external child process could not change its parent.
When should a script use <code>mktemp</code>?
When it needs a temporary file or directory with a collision-resistant name and controlled creation.
Is a zero exit code sufficient acceptance evidence?
No. Also verify the intended file, state or application outcome and its ownership, permissions and content.

Guided lab and acceptance test

  1. Use type -a, help, man and RPM ownership to document five common commands.
  2. Create filenames containing spaces, wildcard characters and a leading dash in a temporary directory; list them without accidental expansion.
  3. Build a three-stage pipeline where the first command fails. Compare status with and without pipefail.
  4. Write a script that requires one validated argument, uses a protected temporary directory and emits normal output and errors separately.
  5. Test the script with an empty value, whitespace, a leading dash and an unexpected path.
  6. Remove only the lab temporary directory after printing and verifying its exact resolved path.

Primary references

Advertisement