Skip to main content
Systems Programming

Shell Filters and System Administration

Published: 2026-08-20
Level: postgraduate
Audience: Postgraduate students in Systems Programming

Prerequisite Knowledge

This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.

Previously Covered in This Subject

  • Creating and inspecting users and /etc/passwd - covered in Lecture 1 (Introduction to Systems Programming)
  • File permissions, rwx triplets, and chmod (absolute and symbolic modes) - covered in Lecture 5 (Linux File System - Inodes, File Types, Permissions and Links)
  • Inter-user communication: who, tty/pts distinction, and wall broadcasting - covered in Lecture 4 (Linux Commands and File System Navigation) and Lecture 1 (Lab in Practice - 24x7 Cloud Host and User Accounting)
  • Linux commands, redirection, and file-system navigation (ls, cat, cp, mv, rm) - covered in Lecture 4 (Linux Commands and File System Navigation)
  • The file principle and directory hierarchy (everything is a file) - covered in Lecture 1 (The File Principle - Everything Is a File, Types and Hierarchical Layout)

12.1 Shell Scripts and Assignment Structure

This session sits at the junction of two threads: the shell as a programming language and the shell as a glue for the operating system. It builds directly on the previous two sessions on shell programming and frames the course assignment, which is not an exercise in printing patterns but a practical automation task.

Hook — why should you care? Imagine you must check 500 user accounts to find who can log in interactively, or scan thousands of log lines for one error code. Doing it by hand in an editor is slow and error-prone. A five-line shell script that calls the right filters does it in under a second. That is the shift this lecture makes: from illustrating language features to using them to get real work done.

Intuition — shell script as a recipe. Think of a shell script — a plain text file that lists shell commands plus programming constructs such as loops and conditionals, executed line by line by a shell interpreter such as bash — like a cooking recipe. The ingredients are individual commands (grep, sort, wc, cut); the method steps are control structures (for, if, while). A recipe that only says "stir five times in a star pattern" teaches technique, but a real recipe combines ingredients to produce a dish. Similarly, a useful script orchestrates filters and utilities to search files, transform text, and automate administration, not merely to print shapes on the screen. Where the analogy breaks: unlike a kitchen, the shell reuses the exact same commands interactively and inside scripts, so fluency on the command line transfers directly into scripts, and errors in a script can affect live system files.

Formalize — what a shell script really contains

A shell script has two layers:

  1. Commands and filters. Every command you can run interactively — grep, ls, date, who — is a program on disk. A script invokes them as building blocks. Filters are a special subclass that reads stdin or files, selects or transforms lines, and writes to stdout, designed to be chained with pipes.
  1. Programming constructs. The shell provides for, while, if/else, case, variables, and functions. The constructs seen in the session illustrate the language features:
for i in 1 2 3 4 5; do
  echo -n "\$i "
done
# → 1 2 3 4 5

and a nested loop that prints a star triangle:

for i in 1 2 3; do
  for j in \$(seq 1 \$i); do echo -n "* "; done
  echo
done

These demonstrate iteration and nesting, but the assignment expects the same constructs applied to purposeful tasks: looping over file lists, testing exit codes, and automating the administrative steps covered from 12.2 onward. Understanding individual commands is therefore central — without knowing what grep -c or grep -w does, you cannot choose the right ingredient for the script.

Worked mini-context: the session contrasted "printing stars" versus "searching files". The former validates syntax; the latter delivers value. A script that counts how many lines in /etc/passwd have /bin/bash as their shell (grep -c "bash\$" /etc/passwd) already does administration that would otherwise require opening the file and counting by eye.

Assumptions & Scope — when this view applies

Assumption: the shell (typically bash on Linux) interprets the script, and each invoked command exists in PATH with expected options. If the shell changes (sh vs bash vs zsh) or a command variant is missing (-w on older grep), behavior may differ.

Scope: shell scripting is ideal for file-centric, line-oriented automation and for gluing existing tools. It is not a replacement for compiled languages when heavy computation, complex data structures, or binary formats are involved. For those, Python or C complements the shell.

What breaks the model: scripts that assume interactive-only commands will behave the same non-interactively (e.g., aliases, prompts) without sourcing the right environment.

Visual intuition: picture a pipeline diagram with two lanes. The top lane shows for → if → echo "*" looping to build a visual pattern — a closed loop producing decoration. The bottom lane shows for file in *.log → grep "ERROR" → wc -l flowing left to right, with files entering at left, filtered lines in the middle, and a count exiting at right. The horizontal axis is data flow, the vertical is purpose. The takeaway: the same loop construct carries very different payloads.

Pitfalls

  • Mistaking illustration for objective. Printing 1 2 3 4 5 or star pyramids demonstrates for syntax but is not what will be assessed. The assignment rewards scripts that embed system commands and interpret their output.
  • Trying to learn scripting without command fluency. Every command used interactively is also a script building block. Skipping the filter chapters (grep, sort, cut) leaves you with control flow but nothing useful to control.
  • Underestimating team coordination. The workload is sized for three; a solo loop over files is easy, but designing, testing, and documenting a complete admin workflow is not. Delay in forming groups directly cuts the preparation time for the command-heavy scripts that follow.

Recap + Bridge — one-line recap and handoff

Shell scripts become practical when programming constructs orchestrate real filters and utilities, not when they merely draw patterns. Hold that framing: the rest of the lecture turns to the most used filter — grep — precisely because it is the command you will call most often inside those scripts. The next section introduces its purpose and syntax.

Real-World & Domain Connection: In operations teams, shell scripts are the overnight operators — log rotation at 2 a.m., daily audit of /etc/passwd for new accounts, or extracting error lines before mailing a report. Civil, mechanical, and CS engineers alike use the same glue role when they automate data cleaning before simulation or analysis. Mastering command-driven scripting is the portable skill that makes the later admin tasks executable without manual intervention.

12.1.1 Role of Shell Programming and Commands

A shell script — a text file of shell commands plus loops and conditionals executed by the shell — becomes practical when it orchestrates filters and utilities rather than drawing shapes. The for loops printing 1 2 3 4 5 and nested star patterns illustrate syntax, but the assessed use is file searching, text transformation, and admin automation, where each interactively practiced command becomes a reusable script component.

12.1.2 Assignment Groups and Practical Focus

The assignment is sized for three students per group, with at most one group of two permitted; all others should have three members. Students not yet on the assignment sheet should form groups promptly and confirm the final list so program allocations can be frozen and preparation time preserved. Early confirmation respects committed peers and aligns with the lecture's pivot to command-heavy scripting, where shared workload matters.

12.2 The grep Filter — Purpose and Syntax

The grep filter purpose — searching passwd without manual edit — illustrates the core value: grep scans passwd without opening the file in an editor, avoiding manual edit risks.

The most frequently invoked building block in shell scripts for text tasks is grep. The session repeatedly heard "grab/grip" — the standard Unix filter grep, short for global regular expression print (g/RE/p in ed), now used far beyond editing to scan any text source.

Hook — the question that motivates grep. You have a file /etc/passwd with hundreds or thousands of accounts. You need to know whether user Anita exists or whether anyone still uses /bin/bash as their shell. Do you open the file in vi and hunt line by line, or ask the system to surface only the lines that contain the pattern? grep is the second answer, and it avoids both manual inspection and unnecessary elevated editing of a sensitive file.

Intuition — grep as a sieve. Picture pouring mixed grain through a sieve that keeps only grains of the size you specified. Input is a stream of lines (from a file, pipe, or stdin); the sieve is your pattern — a description of the characters you want; output is only the lines that contain that pattern. Like a sieve, grep does not modify the input file — it selects and prints. Where the analogy breaks: a real sieve works by physical size; grep works by textual pattern, and with regular expressions that pattern can describe families of strings, not just one literal.

Formalize — filter, pattern, and syntax

A filter reads input (files or stdin), transforms or selects portions, and writes a result to stdout. grep is a selective filter: given a pattern and zero or more files, it scans each line and emits the lines where the pattern occurs, optionally with file names or line numbers.

General form (from grep(1) and T1 §4.1, R2 §3.1):

grep [options] pattern [file ...]
  • pattern — a string of characters. In the simplest case a literal string such as this or case.
  • More generally a regular expression — a pattern language using metacharacters such as . (any single character), * (zero or more of the preceding element), ?, +, | (in extended mode), character classes [A-Z], and anchors ^/\$. The lecture notes most practical uses employ regular expressions: a dot stands for any single character, * repeats, and bracket expressions restrict to alphabets or digits. Options modify matching or output format and are detailed in 12.3.
  • [file ...] — zero or more pathnames; if omitted, grep reads stdin and waits for keyboard input, which is why a missing file argument appears to hang.

Exit status — useful in scripts: 0 if a match was found, 1 if no match, 2 if the file was not found or options are invalid. This makes grep testable in if grep -q pattern file; then ... fi.

Quoting rule: if the pattern contains whitespace or shell metacharacters, quote it ('pattern' or "pattern"); single quotes are safest so the shell does not interpret *, \$, or ^ before grep sees them.

Worked Example — motivating triage on /etc/passwd

You want to know if SAP accounts exist without editing the password database.

grep "SAP" /etc/passwd
# SAP1:x:1005:1005::/home/SAP1:/bin/bash
# SAP2:x:1006:1006::/home/SAP2:/bin/bash

Without grep, you would vi /etc/passwd (which risks accidental edits and may need sudo for read guarantees) and search manually. With grep, the answer appears immediately, even if the file has thousands of entries. The same mechanism applies to any text file — logs, config, or grep_file.

Sense-check: output lines are from the input file verbatim; grep does not reorder or modify them.

Assumptions & Scope — when the syntax applies and when it breaks

Assumption: input is line-oriented text. grep processes each line individually; it does not match across newline boundaries (a pattern never spans two lines).

Assumption: pattern interpretation follows basic vs extended rules. Basic grep treats +, ?, |, () literally unless escaped or -E is used.

Scope: grep is for line selection. Use sed/awk when you need substitution or field-aware transformation, and fgrep/grep -F when you want literal fixed-string search without any metacharacter interpretation.

Failure modes: an unquoted pattern containing shell * may be glob-expanded before grep sees it; a regex starting with - will be parsed as an option unless introduced with -e or --.

Visual intuition: draw a flow left to right: files → grep pattern (sieve) → matching lines to stdout. Annotate the sieve mesh as the pattern: a small literal string lets only exact matches through; a regex mesh with holes shaped ., *, [A-Z] lets families of strings through. The vertical axis is "lines kept vs dropped"; the takeaway is selectivity increases with pattern specificity.

Pitfalls

  • Transcribing the name by sound. "grab/grip" in speech maps to the single tool grep. Writing grab at the command line yields command not found.
  • Forgetting quotes. grep From \$MAIL works, but grep From address \$MAIL without quotes splits the pattern. Use grep 'From ' when the pattern contains spaces.
  • Expecting in-place edit. grep prints to screen; it never changes the input file, unlike sed -i or an editor save.
  • Confusing exit status with output. In a script, grep may print nothing yet succeed (exit 0) if a match was suppressed with -q, or print lines yet fail conceptually if you test the wrong status. Test \$? or use if grep -q.
  • Directory without -r. Passing a directory alone (e.g., grep pattern /etc) reports Is a directory and skips it — you must name files explicitly or use grep -r.

Recap + Bridge

grep is a selective filter that scans lines for a pattern — literal or regular-expression — and prints matches without modifying files, making it the fast alternative to manual editor search. Its exit status makes it script-friendly. With purpose and syntax settled, the next section details the options that reshape what grep reports and how strictly it matches.

Real-World & Domain Connection: System administrators, developers, and data engineers use grep as first triage for log forensics (grep ERROR app.log), config audits (grep "^PermitRootLogin" /etc/ssh/sshd_config), and pipeline filtering (ps aux | grep apache). Combined with pipes, it feeds counts, sorts, and further filters — the exact glue role highlighted in 12.1 for building meaningful shell scripts.

12.2.1 What grep Does

A filter — a program that reads input, transforms or selects parts, and writes a result — in grep's case selects lines containing a given pattern. Given a pattern and a file, grep outputs matching lines, often with file names and line numbers depending on options; with multiple files it indicates which file holds each match. This avoids manual inspection of large files. The motivating example is /etc/passwd with hundreds or thousands of entries: locating a user or group by editing the file is slow and may require privileges, while grep surfaces the answer immediately, and the same mechanism applies to any text file.

12.2.2 Command Syntax and Pattern Types

The general form is grep [options] pattern [file ...] where pattern is a literal string such as this or, more generally, a regular expression — a pattern language using metacharacters ., *, ?, character classes [...], and anchors ^/\$ to describe sets of strings; a dot can stand for any single character, * repeats, and brackets restrict to alphabets or digits. Options modify matching or output and are covered next.

12.2.3 Files, Multiple Files, and Directories

grep accepts a single file, a list, or a shell-expanded set: grep this grep_file searches one file, while grep THIS ./* or grep case *file uses a glob where *file matches any file name ending in file (e.g., grep_file, demo_file) and searches each matched file. When a directory name is passed without a recursive flag, grep reports Is a directory and skips it; to search a directory's files you must give the files themselves via a glob or grep -r.

12.3 grep Options for Output Control

Options — hyphen-prefixed flags that reshape matching and reporting without changing the core search — are what make grep adaptable inside scripts where output format is often more important than raw matches.

Hook — same search, four different answers. Search this in grep_file and you might want: the matching lines, the count 3, just the file names that contained it, or the exact substrings. One pattern, four reporting needs. Options deliver each without changing the pattern.

Intuition — options as output lenses. Think of grep as a camera pointed at text and options as lenses you snap on: -c counts instead of showing, -l shows only file labels, -n adds line-number captions, -i makes the lens color-blind to case, -v shows the negative, -w frames only whole objects, -h removes location stamps. The scene (pattern + files) stays the same; the lens changes what the photograph contains. Break point: lenses compose; some combinations conflict or dominate (e.g., -c suppresses normal line output, -l suppresses line content even if -n is also given).

Formalize — the essential option set (R2 Table 3.2, T1 §4.1)

Option Name Effect Lecture demonstration
-c count Prints count of matching lines, not total word occurrences. A line with two matches still counts as 1. grep -c this grep_file3
-l list files Lists only names of files containing a match, once each, newline-separated. grep -l case *filedemo_file, grep_file
-o only matching Prints only the matched substring, one per line, not the whole line. Isolates pattern for piping to `sort \ uniq`
-h no filename Suppresses file-name prefix when multiple files are searched. Default with ≥2 files is file:line; -h removes it. Contrasts with default multi-file output
-n line number Prefixes each matching line with its line number (n:line). this on line 1 case-sensitive; lines 1,2,3,6,7 with -i
-i ignore case Folds case so lower matches upper. Overlaps historical -y. this (3) vs -i this (5): captures This, THIS, ThIs
-w word Matches whole words as if surrounded by \< \>. Dell vs Dell EMC; North vs Northwest (see 12.5–12.6)
-v invert Prints lines that do not contain the pattern. grep -v this returns empty lines + non-matching
-e expression Explicitly introduces pattern (grep -e -pattern). Needed when pattern begins with - or for multiple -e patterns (OR). grep -e "-error" file
-F fixed string Treats pattern as literal fixed string, no metacharacters (historical fgrep). Fast parallel literal search
-f file of patterns Reads patterns from file, one per line, matches any. grep -f common-errors document
-E extended regex Enables extended regex operators `+ ? \ () (historical egrep`). Adds +, ?, alternation
-s silent Suppresses normal output, shows only errors; useful for exit-status checks in scripts. grep -s pattern file; echo \$?
-b block number Prefixes byte/block offset; historical, occasionally useful for disk context. Rare today

Composition: options combine, e.g., grep -i -c, grep -ivc, grep -n -i THIS. Order after grep does not matter except that -e consumes its argument. The lecture stressed knowing which option yields counts vs lines vs file names, and how -i, -v, -n interact when stacked.

Worked Example — counting and listing on the demo files

Setup: grep_file and demo_file are copies (cp grep_file demo_file) holding words this, This, THIS, case, file, line, plus empty lines. Seven logical lines, three hold literal lowercase this.

Count, case-sensitive:

grep this grep_file        # → 3 lines shown
grep -c this grep_file     # → 3

Case-insensitive expansion:

grep -i this grep_file     # → 5 lines (adds This, THIS/ThIs variants)
grep -i -c this grep_file  # → 5
grep This grep_file        # capital T, no -i → 1 exact-cap match

List vs show:

grep -l case *file         # → demo_file\n grep_file  (both have 3 hits each)
grep case *file            # → demo_file: ... \n grep_file: ...  (lines + filenames)
grep -h case *file         # same lines without filenames

Only matching portion:

grep -o "this" grep_file   # → one "this" per match line, one per line if line repeats

Invert + count interaction:

grep -v this grep_file            # → 2 empty + lines without lowercase this = 4 lines
grep -v -c this grep_file         # → 4
grep -i -v -c this grep_file      # case-insensitive invert → only the 2 truly empty lines = 2

Sense-check: -c counts lines, so a line with this this on one line would still be 1, not 2; -o would emit two lines in that case, revealing the distinction.

Assumptions & Scope

Assumption: input is text lines; binary files may report Binary file matches or need -a.

Scope: use -F/fgrep when metacharacters must be literal and for many parallel literal strings (its run time is largely independent of number of patterns). Use -E/egrep when you need +, ?, | and grouping.

When it breaks: -w is grep-specific and not universal across all Unix variants (SCO noted); overly broad -i can inflate matches unexpectedly (e.g., THIS matching all caps variants when you wanted only one).

Exit-status note for scripts: with -c or -l, success (0) still means "match found", even though normal line output is suppressed — test if grep -q or check \$?.

Visual intuition: imagine a control panel with toggles: -c flips display from lines to a numeric counter; -n adds a ruler margin with line numbers (1: this..., 2: ...); -h/-l toggle file-name labels on/off. The x-axis is lines of the file, y is "shown". With -c the y collapses to a single number 3; with -l it collaps to file names demo_file, grep_file. Takeaway: options do not move the search; they reshape the report for the next pipeline stage.

Pitfalls

  • Confusing -c with word count. grep -c this counts lines containing this (3), not occurrences. A line with case case still counts as 1. Use -o piped to wc -l to count occurrences.
  • Expecting -l to show lines. -l suppresses line content — you get only grep_file. If you need lines plus names, omit -l (default with multiple files) or combine with nothing else that suppresses output.
  • Forgetting -i folds all case. Without -i, ThisthisTHIS; with -i, all five case variants matched (3 → 5). Assess whether exact case matters before adding -i.
  • -w substring vs word. Without -w, Dell matches Dell EMC and North matches Northwest/Northeast as substrings. With -w, only standalone words match. Use -w for precise token search.
  • Inversion double-negatives. grep -v -c counts non-matching lines; grep -i -v -c with case folding changes the count from 4 to 2 in the demo because two lines previously distinct by case are now excluded. Reason carefully about what set you are complementing.
  • Pattern starting with -. grep -error file parses -e as option; write grep -e "-error" or grep -- "-error" instead.

Recap + Bridge

Options reshape matching and reporting — -c counts lines, -l lists files, -o isolates matches, -h/-n control labels and numbers, -i/-w/-v control match strictness, -F/-E/-f choose pattern language — and they compose. The pattern-hunting power comes next: the regular-expression building blocks that options operate upon.

Real-World & Domain Connection: In scripts, grep -c ERROR app.log gates alerts, grep -l "FIXME" src/* finds files needing work, grep -h pattern *.conf merges configs without filename noise, and grep -v "^#" strips comments before counting. Knowing which flag yields a count vs lines vs filenames prevents pipeline bugs where a downstream wc or xargs expects the wrong shape.

Exam note: Combination behavior is directly examinable. Expect output-prediction questions that distinguish counts versus lines versus file names, and that probe how -i, -v, -n interact when stacked (e.g., grep -i -v -c this). Practice tracing a pattern on a given file with and without each flag.

12.3.1 Counting and Listing Matches

Count-c reports the number of matching lines (not words). Searching this in grep_file shows three lines; grep -c this grep_file reports 3, while file or line as patterns give different counts with exact case sensitivity. List file names-l lists only names of files that contain a match; grep -l case *file lists demo_file and grep_file because each holds matches, answering "where" rather than "what". Only the matching part-o prints only the matched substring, one match per line, isolating the pattern for further processing such as sort | uniq -c.

12.3.2 Controlling What Is Displayed

Suppress file names-h removes the file-name prefix when searching multiple files, showing only matching lines (default with multiple files prefixes each line with file:). Line numbers-n prefixes n:line; this appears on line 1 case-sensitively, and with -i on lines 1,2,3,6,7. Case sensitivity-i ignores case, expanding this from 3 to 5 matches to include This/THIS/ThIs; grep -i -c and grep -i THIS grep_file compose. Whole word-w requires a whole-word match; Dell without -w matches Dell EMC as substring, with -w only standalone Dell qualifies.

12.3.3 Inverting and Specifying Patterns

Invert match-v prints lines that do not contain the pattern; for this in grep_file it returns the two empty lines plus non-matching lines, so grep -v -c this grep_file is 4 and grep -i -v -c this grep_file is 2 (only empty lines after case-insensitive exclusion). Expression-e explicitly introduces a pattern, essential when it begins with - or for multiple -e OR patterns. File of patterns-F treats the pattern as a literal fixed string (no regex), and -f file reads patterns from a file one per line matching any. Extended expressions-E enables extended regular expressions with + ? | () beyond the basic set.

12.4 Regular Expression Building Blocks

Regular expressions are the pattern language grep interprets — a compact notation for describing sets of strings, built incrementally in the lecture with live file checks.

Hook — from one literal to infinite families. Searching literally for this finds one string. What if you need "any word that starts with A and ends with anything", or "any price that looks like 5. followed by digits"? Literals cannot express that. Regular-expression building blocks let a short pattern describe an infinite set, and grep tests each line for membership in that set.

Intuition — blueprints, not photographs. A literal pattern is like a photograph of one house — it matches only that house. A regular expression is a blueprint: . means "any material allowed here", * means "repeat the previous piece zero or more times", [A-Z] means "any capital letter allowed here". [A-Z][A-Z][A-Z] is a blueprint for "three capitals in a row". The more flexible the blueprint, the more houses match. Break point: a blueprint that says "zero or more" can match nothing at all — .* matches the empty string — which surprises beginners who expect at least one character.

Formalize — atomic metacharacters and closure

Dot . — any single character. . matches exactly one character position, including letters, digits, punctuation, space and tab (line-oriented, never newline). A single dot therefore consumes one slot. a.c matches abc, a c, a-c but not ac.

Star * — closure (zero or more of the preceding element). * does not mean "any string" by itself; it repeats the immediately preceding atom zero or more times, as long as possible (greedy). Key compositions:

  • .* — dot is any character, star repeats it zero or more times → any string including empty, including spaces/tabs. This is the bridge that connects two fixed fragments across arbitrary intervening text (this.*file).
  • s* — zero or more s characters (this*file means thi + zero or more s + file, e.g., thisfile, thissfile would match if no space intervenes).
  • [A-Z]* — zero or more uppercase letters.

Critical clarification from lecture: * alone does not mean "any string including spaces" in basic interpretation. In this*file without a dot, * applies to the preceding s and was described pedagogically as covering alphanumerics but not space. To bridge a space between this and file you need the dot: this.*file includes spaces. Demonstrated: grep "this*file" grep_file gave no output while grep "this.*file" grep_file matched this ... file.

Character classes [...] — one character from a set. Square brackets define a character class: any one character from the set inside.

  • [A-Z] — one uppercase letter
  • [a-z] — one lowercase letter
  • [A-Za-z] — one alphabetic character
  • [0-9] — one digit
  • [A-Za-z0-9_] — word character (same spirit as \w)
  • [AG] at pattern start — A or G in first position; [ABCD] / [A-D] — any one of those letters
  • [AB].* — first two characters are A then B, then anything (.* bridges remainder)

Repetition on classes: [A-Z]* or [A-Za-z]* applies * to the class → zero or more characters from that class. Distinction: [A-Za-z]* alone matches any alphabetic string (including empty); [A-Za-z] without * matches exactly one alphabetic character. The count of consecutive class atoms matters: [A-Z][A-Z][A-Z] requires three consecutive capitals; grep "[A-Z][A-Z][A-Z]" table.doc probes exactly that. Without -i, lowercased northeast/southeast were excluded; with -i all three-letter alphabetic runs matched.

Coining the distinction: to require at least one character from a class, the classic basic-regex idiom is class followed by class*, e.g., [A-Za-z][A-Za-z]* for one or more letters (extended regex would write [A-Za-z]+).

Anchoring preview: character classes are atoms that compose with anchors (^/\$) and escaping, covered in 12.5.

Worked Example — probing three capitals in table.doc

File table.doc contains lines like NW Northwest 3.98 ..., NE Northeast 3.77 .... The pattern:

grep "[A-Z][A-Z][A-Z]" table.doc

Step-by-step: [A-Z] = one capital (position 1), second [A-Z] = capital at position 2, third [A-Z] = capital at position 3. Only lines with three consecutive capitals at any position match. Initially Northeast/Southeast lowercased to northeast fail the second/third cap requirement, so only abbreviation fields like NW , SW etc. were candidates but length mattered — after adjustment the count clarified. Adding -i:

grep -i "[A-Z][A-Z][A-Z]" table.doc

now folds case, so any three alphabetic characters consecutively at that locale match regardless of case, and all lines matched.

Second check:

grep "[A-Z][a-z]" table.doc

After lowercasing manipulations, only two lines retained a capital followed by lowercase at the tested column, returning two matches. Sense-check: removing one [A-Z] would change the required length, and grep "[A-Z]*" would match even empty strings at every line (often not what you want).

Assumptions & Scope

Assumption: regex is applied per line, not across lines; .* never crosses newline, and the match is substring by default (need ^/\$ to anchor).

Basic vs extended: In basic grep, +, ?, |, () are literals unless escaped or -E is used; [A-Z]* is basic-legal, [A-Z]+ needs -E.

Scope: use [A-Za-z0-9_] or \w for token-like usernames that include underscores/digits; use [^...] negation (next subsection) when you want "anything except".

Failure mode: * applied to a class that can match empty ([A-Z]*) will match zero characters — grep "[A-Z]*" file matches every line (including empty). To force at least one, double the atom as above.

Visual intuition: draw three adjacent boxes labeled [A-Z] [A-Z] [A-Z] forming a stencil. Slide the stencil along a line N o r t h w e s t; only positions where three consecutive slots are all capitals light up. Replace one box with .* — the stencil now has a stretchy middle that can expand to any length including zero. The x-axis is character position, shape is "fixed vs stretchy". Takeaway: dot-star is the stretchy glue; bracket triples are rigid length checks.

Pitfalls

  • Reading * as "any string". * repeats only the preceding element. this*filethis.*file. The former repeats s; the latter bridges with any characters including spaces. This was the live demo that produced no output without the dot.
  • Forgetting .* matches empty. grep "a.*b" file matches ab (zero characters between) as well as a---b. If you need at least one, use a..*b or extended a.+b.
  • Confusing single-character vs string. [A-Za-z] is one letter, [A-Za-z]* is zero or more letters (a string). grep "[A-Za-z]" vs grep "[A-Za-z]*" on a file with numbers will both match lines with any letter, but semantics differ for empty and multi-char expectations.
  • Shell vs regex brackets. The shell also uses [...] for globs, but inside regex [^...] negation syntax and escaping of ]/-/ ^ differs (T1 §4.1 warns about placement of - and ] to avoid ambiguity).

Recap + Bridge

Building blocks: . = one any, * = zero-or-more of previous (so .* = any string), [...] = one of set with ranges and negation. Composition and repetition on classes controls length, and the classic trap is reading * as any string without its preceding dot. With atoms settled, the next section adds position (^/\$), literalization (\), and word constraints, plus shorthands and negation that make these blocks practical.

Real-World & Domain Connection: These atoms underpin every log filter and validator: grep "[0-9][0-9]:[0-9][0-9]" syslog finds timestamps, grep "[A-Z][a-z]*" extracts capitalized words, and grep ".*ERROR.*" is the canonical "contains ERROR anywhere" bridge used before piping to counters and alerts.

12.4.1 Single-Character and Any-String Metacharacters

A dot . matches any single character (space/tab included) and consumes exactly one position. .* combines dot with * (zero or more of preceding) to match any string including empty, bridging two fixed fragments. The lecture emphasized * alone does not mean any string including space: this*file without a dot applies * to the preceding s (described as alphanumerics but not space) and gave no output, while this.*file with dot-star bridges spaces and matched this ... file.

12.4.2 Character Classes and Ranges

Square brackets define a character class — any one character from the set: [A-Z] one uppercase, [a-z] one lowercase, [A-Za-z] one alphabetic, [0-9] digit, [A-Za-z0-9_] word-like, [AG] at start means A or G in position 1, [ABCD]/[A-D] any of those letters, [AB].* means A then B then anything. Applying * to a class ([A-Z]*, [A-Za-z]*) means zero or more from that class. grep "[A-Z][A-Z][A-Z]" table.doc probes three consecutive capitals; without -i lowercased entries are excluded, with -i all three-alpha runs match.

12.4.3 Character Class Shorthands and Negation

Shorthands (often via extended/PERL-compatible modes, cited as extended classes) include \d digit ([0-9]), \D non-digit, \w word character ([0-9A-Za-z_]) useful for usernames with underscores/digits, \W non-word, \s whitespace, \S non-whitespace. Negation inside a class uses a leading caret ^ inside brackets: [^0-9] matches any character that is not a digit, [^A-Z] any non-uppercase; the placement of ^ is decisive, as explored in Q&A.

12.5 Anchors, Escaping, and Word Matching

Position and literalization turn the building blocks into precise tools: anchors say where in the line, escaping says literally that, and word-mode says whole token.

Hook — the same characters, three different jobs for ^. Meet ^. In ^SAP it means "start of line". In [^0-9] it means "not a digit". In SAP^ it is literal ^. One symbol, three meanings depending on where it sits. Mastering that placement is the difference between "find all accounts named SAP1" and "find every line that contains a non-digit" — the exact mix-up the lecture staged live.

Intuition — anchors as bookends, escapes as quotes. Think of ^ and \$ as bookends that do not take shelf space: they assert "this end of the shelf" without occupying a book slot. ^SAP means the line's left bookend is S, bash\$ means the right bookend is h of bash. Escaping with \ is like putting quotes around a wild character: . normally means "any book", \. means "a book literally printed with a dot on its cover". Whole-word mode -w is asking for a book that sits alone between spaces/punctuation, not squeezed inside another title. Break point: bookends consume no characters, so ^\$ matches an empty line, and 5\. is two characters (5 + literal dot), not three.

Formalize — anchors, escapes, word and extended modes

Anchors — zero-width position assertions (T1 Table 4.1, R2 Table 3.1):

  • ^ outside a bracket, at pattern start, anchors to beginning of line. ^SAP matches lines starting with SAP. Demo on /etc/passwd: grep "^SAP" /etc/passwd → only SAP1, SAP2. ^AAnita etc. ^U → the single Ubuntu-related entry.
  • \$ outside brackets, at pattern end, anchors to end of line. bash\$ matches lines ending with bash. On /etc/passwd this isolates interactive shells: grep "bash\$" /etc/passwdroot, ubuntu, SAP1, SAP2, Anita, Anup while nologin entries are excluded. Conversely nologin\$ lists non-interactive system accounts. In basic grep \$ is often escaped as \$ in examples (bash\$) due to shell protection; inside single quotes '\$' and \$ both anchor.
  • Both together require full-line match: ^pattern\$ means the entire line is exactly the pattern. Classic empty-line probe is ^\$.

Escaping metacharacters — \ removes special meaning:

Characters . * [ ^ \$ have regex meaning; to match them literally, prefix with \. The canonical example is a decimal point: . alone is "any char", so a literal . requires \..

  • 5\. → literal 5 followed by literal . — the two-character sequence 5.
  • 5\..5 + literal . + one any character (wildcard dot) → 5. plus at least one more char. In table.doc this hits column four where values like 53.97 or 5.95 contain 5. + digit, but not bare 5 columns.
  • 5\. alone (no trailing dot) matches any word beginning with 5. and continuing arbitrarily; with -w requiring a whole word 5. it produces no output because no word is exactly two characters 5..
  • Reminder: \ escapes the next char, so \. is literal dot, . stays wildcard; similarly \[ literal bracket, \^ literal caret, \$ literal dollar.

Whole-word matching — -w (R2 Table 3.2):

-w matches only whole words, as if the pattern were surrounded by \< \> (or \b in PCRE). Without -w, grep North table.doc matches Northwest, Northeast, Northern as substrings. With grep -w North table.doc only lines where North appears as a separate word (bounded by non-word characters) qualify, so compound directions are excluded.

Extended vs basic — -E, -F (T1 §4.1 fgrep/egrep lineage):

  • Basic regex (default grep): core ^ \$ . * [] and grouping @@BNMATH434bf885a0c24c2daa984466c10f12f9@@ with \1 tags, repetition \{m,n\}.
  • Extended (grep -E, historical egrep): adds + (one or more), ? (zero or one), | (alternation/or), () grouping without backslashes: e.g., egrep "(North|South)west" or grep -E "5\.[0-9]+" for 5. plus one or more digits.
  • Fixed-string (grep -F, historical fgrep): no metacharacters, parallel literal search for many strings.

Shorthands & negation placement (recap from 12.4):

\d[0-9], \w[A-Za-z0-9_], \s whitespace; negated \D \W \S. Negation [^0-9] uses ^ inside as first char after [ to mean "not". [^A-Z] = non-uppercase. Crucially ^ outside means anchor, inside leading means negation.

File-based hunt nuance:

[A-Za-z]* matches any alphabetic string (including empty); [A-Za-z] without * matches exactly one alpha. Therefore grep "[A-Z][a-z]" table.doc after lowercasing returned only two lines with a capital+lowercase pair, while [A-Z][A-Z][A-Z] required three caps.

Worked Example — isolating interactive accounts and literal-dot searches

Interactive shell audit:

grep "bash\$" /etc/passwd    # or grep 'bash\$'
# root:x:0:0::/root:/bin/bash
# ubuntu:x:1000:1000::/home/ubuntu:/bin/bash
# SAP1:x:1005:1005::/home/SAP1:/bin/bash  ... etc.
grep "nologin\$" /etc/passwd
# daemon:x:1:1::/usr/sbin:/usr/sbin/nologin  ... many system accounts

Change one side: grep "^root" /etc/passwd → only the root line; grep "^bits" /etc/passwd → provisioned cohort (see 12.8). Sense-check: bash\$ vs bash — the former excludes lines where bash appears mid-line, the latter would also match comments containing bash.

Literal-dot table probe: File table.doc columns: NW Northwest 3.98 ..., S Southern 5.95 ...

grep "5\.." table.doc
# S   Southern   5.95  6 and 23   →  5 . 9  (literal dot + any char 9)
# W   Western   53.97 ...        →  5 3 . 9 ... but wait? 5\.. matches 5 . 3? Actually 53.97 contains "5" then "3" then "."? No. Correct: 53.97 contains "3.9" not "5..". So line with 53.97 matches because characters "...53.97..." contain substring "3.9"? No. Better: 5\.. on "53.97" matches "3.9"? Confusion. Real demo: 5\.. matched column four where value starts "5." then digit, so "5.95" qualifies (5 . 9). 53.97 also qualifies because it contains "5" at position 1 of "53.97"? Actually "53.97" is "5" + "3" + "." + "9"... The pattern 5\.. needs "5" then literal "." then any char. In "53.97", characters are '5','3','.','9' — there is no "5." adjacency, so it would not match. Lecture's "53.97" example therefore illustrates a different column interpretation; column four values that did match were those literally starting "5." such as 5.95, 5.678.

Clarified: 5\.. needs literal dot immediately after 5; 53.97 lacks that adjacency and would not be hit — the matched rows were those with 5.95 style entries, confirming column four is the source identified by Dinesh. Removing trailing dot to grep "5\." table.doc matches any 5. plus continuation, still column four; grep -w "5\." table.doc then yields no output because no word is exactly 5. alone. Sense-check: escaping decides: 5. vs 5\. — dot wildcard vs literal dot — and trailing . decides "at least one more char after dot" vs "any continuation length".

Assumptions & Scope

Assumption: locale influences [A-Z] range ordering; LC_ALL=C gives ASCII ordering expected in examples.

Scope: anchors are zero-width; they do not consume a character, so ^\$ can match empty lines and ^A.*Z\$ forces the whole line from A to Z.

When to use extended: need + ? | () — use grep -E; need literal ./* — use grep -F or escape; need word boundaries portably — prefer grep -w over manual \< \> which varies by implementation.

Visual intuition: sketch a line as a shelf from ^ (left end) to \$ (right end). Place label SAP glued to left bookend for ^SAP; bash glued to right for bash\$; \. as a dot sticker on a book cover vs . as a transparent field that can be any cover. Whole-word -w draws word-boundary fences (| non-word) around the pattern. The x-axis is character position; y is "must be at edge vs anywhere". Takeaway: moving ^ inside brackets flips its meaning from edge to negation.

Pitfalls

  • Caret placement. ^[0-9] (outside) = line starts with digit → table.doc printed nothing because no line starts with a digit. [^0-9] (inside leading) = any non-digit → every line has non-digits, so all lines match. Swapping placement completely changes output, the exact Q&A trap in 12.6.
  • Dollar escaping and quoting. In double quotes or unquoted, \$ may be expanded by the shell; always single-quote anchors or escape as \$. Write grep 'bash\$' or grep "bash\$" not grep bash\$ unquoted.
  • 5. vs 5\. vs 5\... 5. = 5 + any char (e.g., 5x, 53), 5\. = 5. literal, 5\.. = 5. literal + one any char. Mixing them explains why removing the trailing dot changes from "requires one char after dot" to "any continuation", and why -w with 5\. finds nothing.
  • -w vs substring. Forgetting -w when searching North returns Northwest/Northeast surprises; adding -w when you actually want substrings would miss compounds.
  • Basic vs extended surprise. Writing grep "a+b" file expecting "one or more a" fails in basic mode (matches a+b literal); need grep -E "a+b" or grep "a\+b".

Recap + Bridge

^/\$ assert line edges without consuming characters, \ literalizes metacharacters (\. for a real dot), -w enforces whole-word boundaries, and -E/-F choose regex dialect. The decisive detail is placement — ^ outside vs inside brackets — and escaping — dot vs \.. Armed with these, we can trace the full worked file searches that follow.

Real-World & Domain Connection: Daily admin audits rely on anchors: grep "^bits" /etc/passwd counts cohort accounts, grep "bash\$" separates interactive from nologin\$ system accounts, and grep "5\." style literal-dot escaping is mandatory when grepping version numbers (1\.2\.3), IP addresses (192\.168\.1\.), or CSV decimals where a wildcard dot would over-match.

Exam note: Anchors, escaping, .* vs *, and caret inside vs outside are the pattern-prediction focus. Be ready to trace a given pattern on a provided file and explain why a variant with or without dot/backslash or with moved ^ gives a different result count.

12.5.1 Beginning-of-Line and End-of-Line Anchors

Anchors assert position without consuming characters. ^ outside brackets at pattern start anchors to beginning of line: ^SAP matches lines starting with SAP; on /etc/passwd grep "^SAP" /etc/passwd returns SAP1/SAP2, ^A returns Anita etc., ^U the Ubuntu entry. \$ anchors to end of line: bash\$ matches lines ending with bash, isolating /bin/bash interactive users (root, ubuntu, SAP1 etc.) while excluding nologin, and nologin\$ conversely lists non-interactive accounts. Both may combine (^pattern\$) for full-line matches such as ^\$ for empty lines.

12.5.2 Escaping Metacharacters

Because . * [ ^ \$ are special, a literal match requires \. Matching a decimal point requires \.: 5\. is the two-character sequence 5 + literal dot. In table.doc with columns NW Northwest 3.98 ..., grep "5\.." is read as 5 literal, \. literal dot, . any single char after the dot, thus matching strings starting 5. plus at least one following character (e.g., 5.95) found in column four — identified by Dinesh as the source — but not bare 5. Removing the trailing dot to 5\. means "word begins 5. and may continue", still column four but longer; grep -w "5\." requiring whole word 5. alone then yields no output because no word is exactly 5.. Students are reminded \ escapes the following char: \. literal dot vs . wildcard.

12.5.3 Whole-Word Matching and Extended Expressions

Whole word-w was examined with North: without -w, grep North table.doc also matches Northwest/Northeast as substrings; with grep -w North only lines where North stands alone as a separate word qualify. Extended vs basic — basic regex uses the core set, while extended (-E) adds +, ?, alternation | and grouping () with richer repetition; the lecture listed -E for that and -F for fixed-string matching. File-based hunt[A-Za-z]* matches any alphabetic string, [A-Za-z] alone one alpha; the count of consecutive class atoms controls leading characters, so grep "[A-Z][a-z]" table.doc after lowercasing returned only two lines with capital+lowercase.

12.6 Worked grep Examples — File Searches and Table Document

This is the hands-on core where the filter, options, and regex atoms are exercised together on two deliberately small files and one structured table. The aim is not to memorize outputs but to learn to trace a pattern line by line.

Hook — can you predict the output before running? Given grep_file with seven lines (some with this, some capitalized, two empty) and table.doc with eight compass rows and numeric columns, can you say whether grep -w "5\." prints anything, or why grep "^[0-9]" prints nothing? The lecture used live polling — Dinesh, Rajesh and others answering — to turn output prediction into the examinable skill.

Intuition — grep as a line-by-line judge. Picture each line entering a courtroom where the pattern is the law: the judge (regex engine) answers "does this line contain a substring that fits the law?" independently for each line. Multi-line globs (*file) simply bring more defendants; ^/\$ add "must be at the wall" conditions; -i tells the judge to ignore case; -v asks for acquittals. The table document adds columns visually, but the judge sees only a flat string per line — column position is not encoded in the law, which is why adding a 5. entry in another row made that row match too. Break point: the judge sees lines independently; there is no memory of previous lines or column numbers unless the pattern itself encodes them.

Formalize — the three file sets

grep_file / demo_file — controlled corpus. Created by pasting multi-line content, then cp grep_file demo_file. Identical initial contents containing words this, This, THIS (and ThIs variant), case, file, line, plus two empty lines. Total logical lines ≈7, with three holding literal lowercase this. This small size lets counts be verified by eye via cat.

table.doc — structured columns. Built interactively via cat > table.doc with eight rows:

NW  Northwest   3.98  3 and 34
W   Western     53.97 5 and 23
SW  Southwest   2.78  2 and 18
S   Southern    5.95  6 and 23
SE  Southeast   2.99  6 and ...
NE  Northeast   3.77  63
N   Northern    4.96  5 ...
C   Central     5.678 3 and 45
... (values tweaked live)

Columns: (1) abbreviation, (2) full direction name, (3) decimal d.dd or dd.dd, (4) subsidiary integers. The pattern-hunting value is that column 3 values contain digit . digit, column 4 may contain bare digits, so 5\.. vs 5\. probes which column is hit.

Glob semantics. Pattern *file in shell context means "file name ending in file", so grep case *file expands to grep case demo_file grep_file (desktop directories reported Is a directory and skipped). The pedagogical point: to search a directory's files, give the files via glob, not the directory alone.

Worked Example — basic searches and counts on grep_file

Step 1 — setup:

cat grep_file
# line1: this is a case file.
# line2: This is ...
# line3: THIS ...
# line4: (empty)
# line5: (empty)
# line6: ThIs line has ...
# line7: another case line with file.
# (illustrative; lecture had 3 lowercase this lines)
cp grep_file demo_file

Step 2 — literal search:

grep this grep_file
# → prints the 3 lines containing lowercase "this" as substring

Step 3 — multi-file glob:

grep THIS ./*          # shell expands ./* to all files in .
# demo_file:THIS ...
# grep_file:THIS ...
# grep: ./Desktop: Is a directory (skipped)
grep case *file
# demo_file: this is a case file.
# demo_file: another case line with file.
# demo_file: ...
# grep_file: (same 3 lines with prefix grep_file:)

File names prefix when ≥2 files are searched; with one file they are absent unless -H forced.

Step 4 — case sensitivity ladder (counts are lines, not words):

grep this grep_file      # 3 lines
grep -c this grep_file   # 3

grep -i this grep_file   # 5 lines (adds This, THIS, ThIs)
grep -i -c this grep_file # 5   (also grep -ic)

grep This grep_file      # 1 (exact cap T, no -i)
grep -c line grep_file   # 3 (three lines hold word line)

grep -n this grep_file   # e.g., 1:this is a case file.  (only line 1 in case-sensitive)
grep -n -i this grep_file # 1:... 2:... 3:... 6:... 7:...

Sense-check: -c counts lines with at least one match, so even if a line held this this, it would still be 1; pipeline grep -o this | wc -l would count occurrences.

Step 5 — anchored and composite:

grep "^this" grep_file          # 1 line — only line starting lowercase this
grep -i "^this" grep_file       # 3 lines — folds This/THIS at start
grep "file\$" grep_file          # 0 — lines end "file." with trailing dot, so \$ fails
grep "file\." grep_file         # 2 — lines ending literal "file."
grep "^this.*file\.\$" grep_file # this at start, .* bridges spaces, literal file. at end → matches qualifying line(s)
grep "^this*file" grep_file     # 0 — star repeats s only, cannot bridge space (see Q&A)
grep -h "file" grep_file demo_file  # same lines without filename prefix (vs default with prefix)

Step 6 — table.doc probes (trace each pattern atom):

grep "5\.." table.doc
# → lines where substring "5" + literal "." + one any char occurs
# S   Southern    5.95  ...   (5 . 9)
# C   Central     5.678 ...   (5 . 6)
# Dinesh identified column four as source because column four holds 5.95 style values; 53.97 would not match "5." adjacency

Adding a 5. entry in another row's column two then made that row also match, confirming per-line scanning, not column-number awareness.

grep "5\." table.doc        # any "5." plus continuation
grep -w "5\." table.doc     # no output — no word is exactly "5." alone

grep "^[WE]" table.doc      # lines starting W or E → Western, Eastern entries (Rajesh correct)
grep "^[WE].*" table.doc    # same plus anything after

grep "^[0-9]" table.doc     # 0 — no line starts with digit
grep "[^0-9]" table.doc     # all lines — every line has at least one non-digit
grep "^[^0-9]" table.doc    # all lines — every line starts with non-digit (interface of two uses)

grep "[A-Z][A-Z][A-Z]" table.doc # three caps consecutive
grep -i "[A-Z][A-Z][A-Z]" table.doc # with -i all lines match (folded)
grep "[A-Z][a-z]" table.doc # after lowercasing northeast/southeast, only 2 lines retain Cap+lower at tested position

Sense-check for table: each pattern is tested per line; ^ checks position 1 only; 5\.. requires literal dot adjacency, so bare 5 in column four fails.

Assumptions & Scope

Assumption: files are small and ASCII, locale C; grep is basic mode unless -E/-F specified; shell globs have expanded before grep sees arguments.

Scope: these demos generalize: the same grep -i -c, anchored ^bits, and 5\. escaping apply to /etc/passwd, logs, and CSVs on any line-oriented text.

Limits: patterns do not express column numbers directly — "column four" is a human interpretation; the regex sees only a character stream. For column-aware extraction use awk -F ' ' '{print \$4}' or cut.

Visual intuition: show grep_file as 7 stacked horizontal strips, three highlighted for this (case-sensitive) and five for -i this. Overlay ^this as a left-edge highlighter touching only strip 1; file.\$ as right-edge touching strips ending with dot. For table.doc, draw columns as faint vertical guides but emphasize the regex scans left-to-right across the whole line, so 5\.. highlight falls in column four only where literal 5. occurs. Takeaway: the pattern's yes/no is per strip, not per column box.

Pitfalls

  • Counting lines vs occurrences. -c is lines, not words — a line with two hits counts as one.
  • Star vs dot-star. this*file (star on s) cannot bridge a space; this.*file (dot-star) can. Live demo gave zero vs success, the exact misconception corrected.
  • Empty-line surprises. grep -v this returns two empty lines plus non-matching; students often forget empty lines are lines.
  • End-anchor with trailing dot. file\$ fails when lines end file.; need file\. or file\.\$.
  • Caret placement. ^[0-9] vs [^0-9] confusion — see Q&A; placement outside vs inside brackets flips meaning.
  • Glob vs regex *. *file as shell glob (file name ending in file) ≠ regex * (repetition). Mixing contexts leads to "why does *file work in grep case *file?" — because the shell expands it before grep.
  • Assuming column semantics. 5\.. hitting column four is because that's where 5. happens to appear; adding 5. elsewhere makes another column match, proving no column awareness.

Recap + Bridge

File searches combine corpus (grep_file/demo_file/table.doc), counting (-c/-i/-v), anchoring (^/\$/^.*\.\$), and literalization (\.). The examinable skill is to trace each atom: dot vs literal dot, star vs dot-star, ^ outside vs inside. With worked tracing complete, the next sections pivot from text selection to the privilege model that governs which files you may even read or modify.

Real-World & Domain Connection: The same patterns audit real systems: grep "^2021" /etc/passwd counts cohort accounts, grep "192\.168\." auth.log finds local addresses (literal dots), grep -w ERROR app.log | grep -v -i timeout isolates non-timeout errors. Mastering per-line prediction makes script behavior predictable before deployment.

12.6.1 Basic Searches with grep_file and demo_file

A text file grep_file was created by pasting multi-line content and duplicated via cp grep_file demo_file; both initially held identical lines with this, This, THIS, case, file, line, and empty lines. grep this grep_file returned the three lines containing lowercase this as substring, confirming three occurrences. A multi-file glob such as grep THIS ./* or grep case *file (where *file means any file name ending in file, matching grep_file and demo_file) searched both files, reported desktop entries as Is a directory and skipped them, and prefixed matches with file names; case contributed three matching lines per file with file names included.

12.6.2 Case-Sensitive versus Case-Insensitive Counts

Case sensitivity was contrasted step by step: grep this grep_file → three lines; grep -c this grep_file3; grep -i this grep_file → five lines including This, THIS, ThIs variants; grep -i -c this grep_file (or grep -ic) → 5; grep This grep_file (capital T, no -i) → 1; grep -c line grep_file3. Emphasis: -c counts lines containing at least one match, not total word occurrences — a line with multiple occurrences still counts as one.

12.6.3 Anchored and Composite Pattern Searches

Start anchorgrep "^this" grep_file matches lines beginning this, one line in the sample, demonstrating ^ at line start. End anchorgrep "file\$" grep_file vs grep "file\." grep_file contrasts end-of-line vs literal dot: file\$ alone yields no output because lines end file. with trailing dot; file\. succeeds on two lines ending file.. Both anchorsgrep "^this.*file\.\$" grep_file requires this at start, .* bridging spaces, and file. at end; switching .* to * alone produces no matches because * without dot cannot bridge spaces, reinforcing the .* lesson. Case-insensitive anchoredgrep -i "^THIS" or grep -i "^this" expands start-anchored to three lines. grep "file" locates substring anywhere, grep "^file" restricts to beginning.

12.6.4 Table Document Exploration and Classroom Q&A

A structured file table.doc built via cat > table.doc with rows:

NW  Northwest   3.98  3 and 34
W   Western     53.97 5 and 23
SW  Southwest   2.78  2 and 18
S   Southern    5.95  6 and 23
SE  Southeast   2.99  6 and ...
NE  Northeast   3.77  63
N   Northern    4.96  5 ...
C   Central     5.678 3 and 45
... (values adjusted live)

Columns: abbreviation, full name, decimal with dot, subsidiary integers.

Pattern walkthroughs: grep "5\.." table.doc matches 5 literal, \. literal dot, . any char after — column four source, so 5.95/5.678 rows match but bare 5 does not; Dinesh identified column four and reasoned that it holds 5 before the dot. Adding a 5. entry in another row made that row match, confirming per-line scanning and no column-position encoding. grep "5\." table.doc matches any word beginning 5. with continuation; grep -w "5\." yields no output because no word is exactly 5. alone. grep "^[WE]" table.doc matches lines starting W/E (Western, Eastern — Rajesh); grep "^[WE].*" extends with anything after. grep "^[0-9]" vs grep "[^0-9]" illustrates caret placement: the former prints nothing (no line starts digit), the latter shows all lines (every line has a non-digit). grep "[A-Z][A-Z][A-Z]" table.doc probes three consecutive capitals; after lowercasing northeast/southeast they are excluded, returning only fully capitalized abbreviations; after manipulations only two lines remain for [A-Z][a-z].

Q: What will grep "5\.." table.doc output and which column does it hit? Why does it not show the bare 5 columns? A: It hits column four because column-four values contain 5 immediately followed by a literal dot and then another character — e.g., 5.95 or 5.678 (pattern: 5 literal, \. literal dot, . any following char). Bare 5 without a dot fails the \. requirement, so it is not returned. Adding a 5. entry elsewhere would make that line match too, proving scanning is per-line, not column-aware.

Q: If * means any string, why does grep "this*file" grep_file give no output while grep "this.*file" grep_file succeeds? A: * repeats only the immediately preceding element. In this*file the star repeats the final s (described as alphanumeric repetition but not covering space), so it cannot bridge the space between this and file. .* means "any character, zero or more times", which includes spaces, so this.*file bridges the gap and succeeds. Without the dot, the pattern cannot span the space, hence no match.

Q: Does .. mean parent directory here? A: No. Inside a regular expression .. means two wildcards, each matching any single character. The parent-directory meaning (..) applies to filesystem paths, not to the pattern argument of grep. Confusing the two caused the error for grep "5\..", where the suffix dots are regex wildcards, not directory navigation.

Q: What does each piece of 5\. and 5\.. mean? A: 5 is literal five. \ escapes the next character so \. matches a literal dot, not the wildcard. A trailing . without backslash is the wildcard for any single character. Thus 5\. matches the two-character sequence 5 plus dot. 5\.. matches 5 plus dot plus one additional arbitrary character after the dot.

Q: Why does grep -w North table.doc behave differently from grep North table.doc? A: Without -w, North is found as a substring inside Northwest, Northeast, Northern. With -w, the whole-word condition requires North to appear as a separate word bounded by non-word characters, so compound direction names are excluded and only a line with standalone North qualifies.

Q: What about grep "^[0-9]" versus grep "[^0-9]" on table.doc? Why does the second show all lines? A: ^[0-9] with ^ outside the class asserts beginning-of-line followed by a digit. No line in table.doc starts with a digit, so it prints nothing. [^0-9] with ^ inside as leading char negates the class: "any character that is not a digit". Every line contains non-digit characters, so all lines match. The placement of ^ completely changes the meaning — the exact terminology contrast the lecture highlighted.

Teaching moments preserved

  • Misconception correction — * vs .*: Students read * as "any string including spaces". The correction: * repeats only the preceding char; .* is needed to include spaces. Demo this*file (fail) vs this.*file (success) cements the distinction.
  • Rejected analogy — .. as parent directory: The filesystem meaning was rejected inside regex; .. is two wildcards. Source of error for 5\.. suffix was flagged explicitly.
  • Terminology contrast — ^ outside vs inside []: ^ outside = anchor (beginning of line), ^ as first char inside [^...] = negation (not in set). The paired grep "^[0-9]" vs grep "[^0-9]" demo is the canonical illustration.

12.7 Superuser Status and Privileged Operations

Text selection is only useful if you are allowed to read the files — and many operations require more than reading. The session pivots from filters to the privilege model, demonstrated live on a local virtual machine via SSH because shared educational hosts intentionally withhold superuser rights.

Hook — who may change the clock? Anyone can run date to read the time. Try date -s "2022-11-05" to set it and you are denied. The same binary, the same file system, but mutation is gated. The superuser is the role that passes that gate — and the lecture proves it by flipping the calendar from Nov 6 to Nov 5 and back with sudo.

Intuition — superuser as master key. Picture a building where every room (file, process, clock, network) has a lock. A regular user has a key to their office and shared lobbies. The superuser (root, UID 0) is the master key: it opens any room, changes any lock, and evicts any occupant. su means stepping into the master-key office (becoming another user, default root) and staying there; sudo means borrowing the master key for one specific errand, authenticated by your own password, then returning it. The prompt changing from ubuntu@...\$ to root@...# is the visual cue you now hold the master key. Break point: power without discipline destroys: running rm -rf / as root erases the system, whereas the same command as a regular user is contained by permissions — the classic R1 §13 cautionary tale.

Formalize — su and sudo

Identities:

  • su (switch user / substitute user) spawns a shell as another user. With no argument it targets root and prompts for the target's password. Variants:
  • su — switch to root but retain the original user's environment (pwd and \$USER stay where you were).
  • su - or su -l or su root - — switch to root with a login environment: home becomes /root (Linux) or / (classic Unix), \$USER becomes root, and startup files for root are sourced. Demonstrated via pwd; echo \$USER; id comparisons (R1 §13.1).
  • su alice — as root, switch to alice without needing her password; as a regular user, you must enter her password.
  • sudo (superuser do) executes a single command with superuser privileges, prompting for the invoking user's password (if permitted in /etc/sudoers). It does not require the root password. Idioms:
  • sudo command — one-off elevation (e.g., sudo date -s ..., sudo adduser bits1).
  • sudo su — use sudo to authenticate as yourself, then su to become root without needing the root password — the idiom used in the live demo to gain a root shell.
  • sudo -i / sudo -s — interactive variants.

Visual cue: shell prompt # traditionally indicates UID 0; \$ indicates regular user. id confirms: regular user uid=1000(ubuntu), after elevation uid=0(root) (course VM showed id with UID/GID and groups).

Warning from lecture: on shared educational hosts, sudo/su may fail with permission denied or user not in sudoers because privileges are not granted. That is why the demo used a local VM where the instructor controlled sudoers.

Capabilities unlocked once superuser (enumerated in session):

  • Read, write, delete any file, override write-protected directories, change permissions (chmod), group (chgrp) and owner (chown).
  • Create or terminate any process (kill, ps as controller).
  • Change any user's password without knowing the existing one (passwd alice as root).
  • Broadcast with wall, set system clock with date -s, limit resources with ulimit, control scheduling (at, cron via /etc/cron.allow/deny), control network services (FTP, SSH configs), manage hardware and service state.

This is scope, not invitation — do not attempt on unprivileged hosts.

Worked Example — privilege-gated date change (the live demo)

date
# Sun Nov  6 10:15:00 UTC 2022   (read succeeds for anyone)

date -s "2022-11-05"
# date: cannot set date: Operation not permitted   (no sudo → denied)

sudo date -s "2022-11-05"
# [sudo] password for ubuntu:  (you authenticate as yourself)
# Sat Nov  5 00:00:00 UTC 2022   (mutation succeeds)

date
# Sat Nov  5 00:00:00 UTC 2022

sudo date -s "2022-11-06"
# Sun Nov  6 00:00:00 UTC 2022   (restored)

Additional probes:

who
# ubuntu  pts/0  2022-11-06 09:00 (192.168.1.10)  ← SSH pseudo-terminal
# bits1   tty1   2022-11-06 09:05
id
# uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),27(sudo)
id bits1
# uid=1005(bits1) gid=1005(bits1) groups=1005(bits1)

who lists logged-in users with device; id and whoami show effective identity for the current shell. Sense-check: reading date never needs privilege; writing the clock always does — the same binary, different operation, opposite permission outcome.

Assumptions & Scope

Assumption: sudo policy in /etc/sudoers permits the invoking user; su requires target password knowledge (or is bypassed via sudo su when you have sudo).

Scope: use elevation only for the task that needs it, then exit the root shell promptly. R1 §13 warns: do not work routinely as root — log in as a regular user, elevate for the specific command, then return. Prolonged root shells amplify typo damage.

Failure mode: su - changes directory to root's home, which may confuse a script that assumed the original pwd; prefer sudo command or su (without -) when you must retain the caller's working directory.

Visual intuition: graph privilege as a vertical bar: bottom 0–50% regular user (can read own files, run filters), top 50–100% superuser (can mutate any file, clock, passwords). date (read) sits at 10%; date -s (write) sits at 90% with a gate; sudo is the elevator that temporarily lifts you, su is the room you enter. The x-axis is operations, y is required privilege. Takeaway: daily practice keeps you at the bottom; you ride up only for the one operation at the top.

Pitfalls

  • Trying sudo on a shared host that does not grant it. Failure is expected policy, not a bug; use a local VM or request access.
  • Confusing su and sudo passwords. su asks for the target's (root's) password; sudo asks for yours. Entering the wrong password repeatedly locks or denies.
  • Staying root after the task. Forgetting exit leaves a # shell where rm or chmod can damage system areas. Make whoami/id checks habitual.
  • Using su without - when environment matters. Some admin scripts depend on root's PATH/HOME; su without - retains the caller's environment and may fail to find binaries or write to the wrong location. Conversely su - moves pwd to /root, surprising file-relative commands.
  • Over-broad sudo for pipelines. sudo grep pattern file | sort elevates only grep; if sort needs privilege, elevate the pipeline explicitly or run a root shell.

Recap + Bridge

su switches user (full stay; su - takes full login environment), sudo borrows superuser power for one command (authenticated as yourself), and the prompt/id confirm the transition. Once superuser, you control files, processes, passwords, clock, and services — demonstrated live by gated date mutation. With privilege understood, the next section applies it to the canonical admin task: creating and inspecting users.

Real-World & Domain Connection: Every ops team lives in this model: developers use sudo systemctl restart app, data engineers use sudo mount for datasets, and instructors provision cohorts via sudo newusers (next section). The discipline — regular user by default, elevate only for the gated operation, exit immediately — is the production safety habit that prevents outages.

12.7.1 su and sudo — Acquiring Elevated Privileges

su — switch user: su without arguments attempts to become root and prompts for the target account's password; su - or su root switches with superuser environment (home, \$USER, startup). sudo — execute one command as superuser, prompting for the invoking user's password rather than root's; sudo su switches to root after a sudo authentication, the idiom used in the demo, while sudo command runs exactly that command as superuser without a persistent switch. The spoken distinction — su changes the user, sudo grants extra privileges for one command — captures practice, and the prompt change from ubuntu@... to root@... confirms transition. A warning notes that on shared educational hosts sudo/su may fail for lack of granted privileges, hence the local VM.

12.7.2 Capabilities Available to the Administrator

Once superuser, the administrator comprehensively controls the system: modify any file's contents/permissions/ownership even in write-protected directories and delete any file; create or terminate any process; change any user's password without knowing the old one; broadcast via wall; set clock/calendar with date; limit file sizes with ulimit; control scheduling services at/cron; control networking tools FTP/SSH; manage hardware, network, and service state. This enumerates scope, not an invitation on unprivileged hosts.

12.7.3 Demonstration with Date Setting and Privilege Checks

The date command illustrates a privilege-gated mutation: date shows Sun Nov 6, sudo date -s "2022-11-05" sets to Sat Nov 5, date confirms, and sudo date -s "2022-11-06" restores. Without sudo, setting the clock is denied; with sudo, it succeeds. Additional visibility includes who (users with tty/pts devices) and id (UID, GID, membership).

12.8 User Management and Bulk Creation

Creating users is the first admin workflow where sudo pays for itself — and where the choice between one-by-one and bulk creation determines whether provisioning a cohort takes an afternoon or a second.

Hook — 26 accounts, one command or 26? The lecture provisioned 26 new users whose logins start 2021.... Doing that with interactive adduser means 26 password prompts and 26 confirmation cycles. With newusers it was one file plus one command, completing in a fraction of a second. Which would you choose the night before term starts?

Intuition — artisan vs assembly line. Think of adduser/useradd as an artisan crafting one chair: measured, interactive (password, GECOS, home), good for a bespoke account. newusers is an assembly line: you lay out a parts list (a text file where each line is login:password:UID:GID:GECOS:home:shell) and the line builds all chairs at once. The artisan gives feedback per piece; the line gives speed and consistency for batches. Break point: the line is strict about format — a missing colon or field shifts all subsequent fields — whereas the artisan prompts interactively to avoid that error.

Formalize — single-user tools and the passwd structure they populate

Single-user creation:

  • adduser (Debian/Ubuntu family, friendlier, interactive prompts for password and metadata) and useradd (lower-level, script-friendly, e.g., useradd -m -s /bin/bash bits2) both create a new account. The demo used them interchangeably: sudo adduser bits1, sudo useradd bits2, each followed by setting a password (passwd interactive, pattern bits1/bits1 for teaching).
  • passwd changes a password; as superuser you can change any account's password non-interactively and without knowing the old one.
  • userdel removes an account. Demo: sudo userdel bits4 (no extra prompt), then su bits4 fails Login incorrect, confirming removal. Without -r, userdel preserves the home directory; userdel -r would remove home and mail spool.

Four demonstrators bits1bits4 were created individually to illustrate the interactive path.

Structure of /etc/passwd (seven colon-separated fields, T2 Ch 14 / R1 Ch 13):

  1. Username — login name (bits1, root, ubuntu, SAP1, Anita)
  2. Password field — x (encrypted password lives in /etc/shadow, readable only by root, see R1 §13)
  3. UID — numeric user ID
  4. GID — numeric primary group ID
  5. GECOS/comment — descriptive field, often empty or full name
  6. Home directory — absolute path, e.g., /home/bits1
  7. Login shell — e.g., /bin/bash or /usr/sbin/nologin for non-interactive system accounts

Inspection with grep (never edit directly):

grep "^bits" /etc/passwd          # all bits accounts
grep -c "^bits" /etc/passwd       # count of matching users
grep "bash\$" /etc/passwd          # interactive shells
grep "nologin\$" /etc/passwd       # system accounts without interactive login
grep "^A" /etc/passwd             # Anita
grep "^U" /etc/passwd             # Ubuntu login

Auditing with ^/\$ is faster and safer than opening /etc/passwd in an editor, where a typo can lock out logins and where reading historically required elevated care.

Bulk creation with newusers:

newusers reads a text file describing many accounts and creates them in one pass, in the passwd field format. The teaching file Users (variants exist) held 26 entries prefixed 2021... (cohort identifiers), each line like:

20210001:password:UID:GID::/home/20210001:/bin/bash
# compact form: login:password:UID:GID:GECOS:home:shell
# teaching variant: 20210001:pass:1001:1001::/home/20210001:/bin/bash

Field count may be four or more colon-separated values; unspecified fields take defaults. Execution:

sudo newusers Users

Completed in a fraction of a second. Verification mirrors single-user auditing:

grep "^2021" /etc/passwd      # 26 new accounts listed, each with x field, UID, shell
grep -c "^2021" /etc/passwd   # 26

The note that earlier-provisioned names in the environment used the same pathway connects the exercise to visible cohort accounts, showing this is not a toy command but the actual provisioning route.

Worked Example — compare loops vs newusers (trace the cost)

Interactive loop (conceptual, not run 26 times live):

for u in 20210001 20210002 ... 20210026; do
  sudo adduser \$u   # prompts for password, GECOS per user → ~26× interactive steps
done
# If each adduser takes ~30s of prompts, ~13 minutes plus inconsistency risk

Bulk path (actual demo):

cat Users
# 20210001:cohort2021:1101:1101::/home/20210001:/bin/bash
# 20210002:cohort2021:1102:1102::/home/20210002:/bin/bash
# ... 26 lines
sudo newusers Users
# <1 second, no per-user prompts
grep -c "^2021" /etc/passwd
# 26

Removal check:

sudo userdel bits4
su bits4
# su: user bits4 does not exist  (or Authentication failure)
ls -ld /home/bits4
# still exists because -r was not used

Sense-check: bulk creation guarantees uniform shells, homes, and password placeholders; manual loops risk typos in UID/GID or shell paths that are tedious to audit later. After bulk creation, grep "^2021.*bash\$" would confirm all 26 received /bin/bash if that was the intended shell.

Assumptions & Scope

Assumption: you have superuser via sudo/su; newusers requires it to write /etc/passwd//etc/shadow atomically.

Scope: newusers is for batch provisioning (semester start, lab onboarding, enterprise cohorts). For a single ad-hoc account, adduser's interactivity and home-creation defaults are more ergonomic.

Format caution: Users file is colon-separated, not space-separated; a missing : silently misaligns fields. Validate with awk -F: 'NF<7' or grep -c "^2021" /etc/passwd after run.

Cleanup: userdel without -r leaves home for forensic recovery; with -r it removes home and spool — choose deliberately.

Visual intuition: draw a timeline left to right: single-user path has 26 sequential boxes (adduserpasswdmkdir home repeated), total width long; bulk path has one tall box labeled newusers Users whose height is 26 accounts but width is one command. The y-axis is accounts created, x is wall time. The horizontal line at grep -c "^2021" = 26 is the checkpoint. Takeaway: batch collapses time without changing outcome, provided the input file format is correct.

Pitfalls

  • Editing /etc/passwd by hand. Even with sudo, manual vi /etc/passwd risks syntax errors that can lock the system. Prefer adduser/useradd/newusers and grep for inspection.
  • Confusing adduser vs useradd. adduser (Debian) is interactive and creates home by default; useradd is lower-level and may need -m to create home and -s to set shell. Using the wrong one without flags yields accounts with missing homes or sh instead of bash.
  • Forgetting password setup. useradd without passwd leaves an account locked; bulk file must contain a password field (or !/* to lock). The teaching bits1/bits1 pattern is for demo, not production.
  • Malformed bulk file. Missing colons or fields in Users cause UID/GID/home to shift — e.g., 20210001:pass:1101 may default remaining fields unexpectedly. Lint the file before sudo newusers.
  • Assuming userdel cleans home. It does not unless -r is supplied; orphaned homes accumulate. Conversely userdel -r as root is destructive — double-check the username.
  • Not verifying after bulk. Always grep -c "^2021" /etc/passwd and spot-check grep "^20210001" /etc/passwd to confirm shell/home; a silent partial failure is worse than no creation.

Recap + Bridge

Single-user tools (adduser/useradd, passwd, userdel) handle bespoke accounts; /etc/passwd's seven colon fields explain what they write; newusers scales the same format to batch speed — 26 accounts in one command, verified with grep "^2021". With users existing, the next question is collaboration: how groups and file permissions let those users share safely.

Real-World & Domain Connection: Enterprise onboarding, lab provisioning, and cloud VM fleet setup all use batch creation — HR exports a CSV, a script converts it to newusers format, and verification is grep -c "^20" /etc/passwd plus id spot checks. The same /etc/passwd with bash\$/nologin\$ auditing separates human interactive users from service accounts during security reviews.

Exam note: user creation distinctions — adduser/useradd vs newusers, primary vs secondary groups (expanded in 12.9), and userdel semantics — are conceptual comparison and scenario questions. Be ready to choose the right tool for "one bespoke account" vs "26 cohort accounts" and to trace grep "^bits" counts.

12.8.1 Creating and Removing Single Users

adduser (Debian family, interactive) and useradd (lower-level) create a new account; the demo used both (sudo adduser bits1, sudo useradd bits2), each receiving a password via passwd-style prompts (bits1/bits1 teaching pattern). passwd changes a password, and superuser can change any account non-interactively. userdel removes an account — userdel bits4 with no extra prompt, after which su bits4 fails Login incorrect; without -r the home directory is preserved. Four demonstrators bits1bits4 were created individually.

12.8.2 Structure of /etc/passwd

Inspected with grep to avoid paging, /etc/passwd has seven colon-separated fields: (1) username, (2) field x (real encrypted password in /etc/shadow), (3) UID, (4) GID (primary group), (5) GECOS/comment, (6) home directory, (7) login shell (/bin/bash or /usr/sbin/nologin). Examples: grep "^bits" /etc/passwd lists bits1bits4 with x, UIDs, and shell; counting via grep -c "^bits", separating interactive grep "bash\$" vs nologin\$; ^A finds Anita, ^U finds Ubuntu. Auditing with ^/\$ is faster and safer than editing the file directly.

12.8.3 Bulk Creation with newusers

Creating dozens one by one is tedious and error-prone; newusers reads a text file mirroring passwd fields and creates them in one pass. The teaching file Users contained 26 entries prefixed 2021..., each line like 20210001:password:UID:GID::/home/20210001:/bin/bash (four or more colon values: login, password, UID, GID, home, shell). Execution sudo newusers Users completes in <1s; verification grep "^2021" /etc/passwd shows 26 accounts and grep -c "^2021" is 26. This contrasts with looping adduser, saving repetition and ensuring consistency, and the provisioned names earlier in the environment used this pathway.

12.9 Groups and Permission Delegation

Users exist; collaboration requires groups. This section turns individual accounts into teams and makes a shared document writable by that team — surfacing the classic trap where group membership alone is not enough.

Hook — why did Anita's write fail? Anita was added to group bits, the file's group was changed to bits with chgrp, yet echo hello >> bits_doc as Anita was denied. Group membership was correct — id Anita showed bits — but the file's mode lacked group-write. Membership answers who the permission applies to; chmod answers whether that who may write.

Intuition — primary uniform vs club badge. Think of a primary group as the uniform you wear every day: every user is created with one, typically sharing the username (bits1 in group bits1), and files you create default to that uniform. A secondary (supplementary) group is a club badge you pin on: you keep your uniform, but the badge grants club-room access whose door is labeled with that club's name. Unless the room's door (chgrp) is labeled with the club and the lock permits club members to write (chmod g+w), the badge alone does not open it for writing. Break point: you can wear many badges, but a file has only one group label at a time; changing it to bits removes the previous group's label.

Formalize — groups, their files, and the collaboration sequence

Primary vs secondary:

  • Primary group — created with the user account; every user belongs to at least one group. Files the user creates default to this primary group. Typically GID equals UID and name equals username (bits1: x:1005:1005 means user bits1 with primary GID 1005, group bits1).
  • Secondary (supplementary) groups — additional groups added after creation. Membership grants the group's permissions on shared resources without changing primary affiliation. Files remain owned by primary unless chgrp or newgrp is used. Explains permission observations: even though amazon was in secondary group bits, writing failed until chgrp and chmod were corrected.

Structure of /etc/group (four colon-separated fields, parallels passwd):

  1. Group name — AWS, bits, Anita
  2. Password field — x (group password rarely used)
  3. GID — numeric group ID
  4. Member list — comma-separated usernames, possibly empty

Example: AWS:x:999:bits1,bits2,ubuntu means group AWS GID 999 with members bits1,bits2,ubuntu. IDs 0499 reserved for system groups; user-defined groups should use 500 and above (lecture specified 499 cut-off, 999 as safe example; some modern systems use 1000+).

Creation:

sudo groupadd -g 999 AWS
sudo groupadd -g 1016 bits

Verification:

grep "AWS" /etc/group      # AWS:x:999:...
grep "^bits" /etc/group    # bits:x:1016:...

Initially member list empty before users are added.

Membership operations:

sudo adduser bits1 AWS     # Debian idiom adds bits1 to supplementary group AWS
sudo adduser bits2 AWS
sudo gpasswd -a bits1 AWS  # alternate explicit form (add)
sudo gpasswd -d user group # remove user from group
sudo groupdel groupname    # remove group itself

After adding: grep AWS /etc/groupbits1,bits2,ubuntu; id bits1uid=... gid=... groups=...999(AWS) confirming supplementary membership. Manual editing of /etc/group is discouraged; use commands.

Permission triplet & the bits_doc workflow (the main lab):

Goal: users Anita, Anup, and amazon should have write access to shared file bits_doc (rendered tempfile in one run) via shared group bits.

Steps demonstrated:

  1. Create users (if not existing):
sudo adduser anita
sudo adduser anup
# each in primary groups anita, anup
  1. Create shared group:
sudo groupadd -g 1016 bits
  1. Add users to supplementary group:
sudo adduser anita bits
sudo adduser anup bits
sudo adduser amazon bits
  1. Inspect:
ls -l bits_doc
# -rw-r--r-- 1 amazon amazon  ... bits_doc   (group amazon before change)
  1. Change file's group:
sudo chgrp bits bits_doc   # or chgrp bits tempfile
ls -l bits_doc
# -rw-r--r-- 1 amazon bits  ... bits_doc   (group now bits, but mode still r-- for group)
  1. Grant group write:
sudo chmod g+w bits_doc    # or chmod 664
ls -l bits_doc
# -rw-rw-r-- 1 amazon bits  ... bits_doc   (group now rw-)

Trap: default rw-r--r-- gives read for group but not write; without chmod g+w, secondary members can only read (r--r--r-- in one demo run). The triplet is user-group-other each rwx, so after chmod g+w it becomes rw-rw-r-- allowing group write. Membership alone never grants access — the mode must consent.

Probing group membership:

grep "^bits" /etc/group    # shows AWS:999 etc.
id bits1
grep "^bits" /etc/passwd   # cross-check users vs groups

Worked Example — why the first attempt failed and the fix (full trace)

Initial state:

groups anita
# anita : anita    (only primary before adding)
sudo adduser anita bits
groups anita
# anita : anita bits  → now in bits as secondary
ls -l bits_doc
# -rw-r--r-- 1 amazon bits 0 ... bits_doc

anita tries to write:

sudo -u anita bash -c 'echo "hi" >> bits_doc'
# bash: bits_doc: Permission denied

Diagnosis: mode is rw-r--r-- → user amazon has rw-, group bits has r--, other r--. Even though anita is in bits, group permission is read-only.

Fix:

sudo chmod g+w bits_doc
ls -l bits_doc
# -rw-rw-r-- 1 amazon bits ... bits_doc
sudo -u anita bash -c 'echo "hi" >> bits_doc && echo success'
# success
cat bits_doc
# hi

Second probe: id bits1 showed membership in AWS and grep AWS /etc/group listed bits1,bits2,ubuntu, yet su amazon write test without g+w still failed — same trap. Sense-check: after chmod g+w, ls -l must show rw- in the middle triplet; if it still shows r--, the chmod did not apply (wrong file or missing sudo).

Group ID policy:

sudo groupadd -g 999 AWS   # 999 >499 so allowed as user-defined, OK
sudo groupadd -g 400 mygrp # would collide with reserved 0-499 system range

Takeaway: choose GID ≥500 (lecture rule) to avoid system collision.

Assumptions & Scope

Assumption: adduser group-adding semantics (Debian) vs usermod -aG (portable) — lecture demoed adduser user group which on some distros maps to usermod; prefer gpasswd -a or usermod -aG for portability.

Scope: group collaboration scales to shared directories (set chmod g+s for setgid, not shown but natural extension) and to service groups (docker, sudo). The pattern chgrp + chmod g+w is the minimal team-write recipe.

When it breaks: file system mounted with restrictive acl or umask 007 may override; NFS may squash root and map groups via idmap.

Visual intuition: draw a Venn of users Anita, Anup, amazon overlapping in circle bits. Draw file bits_doc with a luggage tag labeled group: bits and a three-slot lock rwx | rwx | rwx. Before fix: tag says bits but lock's middle slot is r-- (read only). After chmod g+w: middle slot flips to rw-, overlap members now open the middle lock. X-axis is membership, Y is permission bits; takeaway: both dimensions must align for write.

Pitfalls

  • Membership without mode. Adding to bits and running chgrp is insufficient; without w bit for group, write is denied. Must chmod g+w.
  • Thinking chgrp changes primary group. It changes the file's group, not the user's primary. User's new files still default to primary unless newgrp is used.
  • Manual editing of /etc/group. Direct vi risks comma misplacement or duplicate GID; use groupadd/gpasswd.
  • GID collision. Using <500 for user groups collides with system groups; lecture mandates ≥500 (example 999, 1016).
  • Forgetting re-login or newgrp. Group membership for an existing shell may not refresh until re-login or newgrp bits; id may show new groups but current shell's effective groups may lag.
  • Confusing file name variants. bits_doc vs tempfile in the session are the same role: the shared document. Apply logic to the name at hand.
  • Assuming one group per user. Every user has primary plus potentially many secondaries; id lists all, groups summarizes.

Recap + Bridge

Primary = default uniform (one per user, owns new files); secondary = club badges (bits, AWS) that grant shared access only when the file's group is set via chgrp and its mode permits write via chmod g+w (rw-rw-r--). GIDs ≥500 avoid system collision; membership is verified with id/grep /etc/group. With sharing solved, the final operational skill is session awareness — who is logged in where, and how to reach them.

Real-World & Domain Connection: Team directories on shared servers, CI artifact folders, and lab submission drop-boxes all use this pattern: groupadd devs, gpasswd -a alice devs, chgrp devs /srv/shared, chmod 2775 /srv/shared (setgid so new files inherit devs). Auditing with grep team /etc/group plus ls -l checks is the routine access-review loop before releases.

12.9.1 Primary and Secondary Groups

Linux distinguishes two associations: Primary group — created with the user, every user belongs to at least one, typically sharing the username (bits1 in bits1), and new files default to it. Secondary (supplementary) groups — additional groups added after creation; membership grants that group's permissions on shared resources without changing primary affiliation, and files remain owned by primary unless chgrp/newgrp is used. This explains write failures despite membership: the effective file group was not the secondary until explicitly changed.

12.9.2 Structure of /etc/group and Group Creation

/etc/group parallels passwd with four colon fields: (1) group name (AWS, bits, Anita), (2) field x, (3) GID, (4) comma-separated member list. Example AWS:x:999:bits1,bits2,ubuntu with GID 999. IDs 0499 reserved for system groups; user-defined groups should use ≥500 (lecture cutoff 499, safe example 999). Creation via sudo groupadd -g 999 AWS, groupadd -g 1016 bits; verification grep "AWS" /etc/group, grep "^bits" /etc/group initially shows empty member list.

12.9.3 Adding Users to Groups and Managing Membership

Membership shown as sudo adduser bits1 AWS (supplementary), adduser bits2 AWS, alternate gpasswd -a bits1 AWS, removal gpasswd -d user group, and groupdel groupname to remove group. After adding, grep AWS /etc/group shows bits1,bits2,ubuntu and id bits1 reflects supplementary group. Probes via grep passwd/group and id per user; manual file editing discouraged.

12.9.4 The bits Document Task, chgrp, and Write Access

Goal: Anita, Anup (and Amazon in interactive attempt) should write to bits_doc/tempfile via shared group bits. Steps: 1) sudo adduser anita, adduser anup (each in own primary), 2) sudo groupadd -g 1016 bits, 3) sudo adduser anita bits, anup bits, amazon bits, 4) ls -l bits_doc shows owner/group/permissions (before change group amazon), 5) sudo chgrp bits bits_doc, 6) chmod g+w bits_doc. Trap: after chgrp, ls -l showed r-- for group and amazon write denied; default rw-r--r-- gives group read not write, so secondary members could only read; permission triplet rwx for user-group-other means rw-rw-r-- after chmod allows group write. Membership alone insufficient — mode must grant it.

Q: If I add Anita to group bits and run chgrp bits bits_doc, why can she still not write? A: Membership makes Anita a principal for the file's group bits, but the permission bits may still be r-- for group. Membership selects which rwx triplet applies; the w bit itself must be present. chmod g+w bits_doc (or mode 664/660 that includes group write) is required before secondary members gain write.

Q: Is the bits group attempt working for bits1? A: id bits1 shows bits1 is a member and grep AWS /etc/group confirms members, but file-level ls -l and su amazon write attempts without group-write demonstrate the missing w bit on the shared file — the demonstration paused to discuss permission triples (rwx for user, group, other) before retrying with chmod g+w.

Teaching moment preserved

Warning — group membership alone insufficient without group-write bit: The lecture deliberately staged a failing write after correct chgrp to force the realization that ACL is two-factor: who you are (group) × what the file allows (mode). The correction — adding w for group — is the durable lesson, not an incidental permission tweak.

12.10 Session Awareness, Communication, and Workspaces

Administration ends with awareness: who is on the system, where they are attached, how to reach them, and how the system multiplexes simultaneous logins.

Hook — five logins, one human. who on the demo machine showed ubuntu on pts/0, plus bits1 on tty1, bits2 on tty2, bits3 on tty3, bits4 on tty5, and even bits5 and tty7 graphical. One physical machine, six simultaneous sessions. How does Linux keep them separate, and why does your SSH session say pts while the local console says tty?

Intuition — rooms vs phone extensions. Think of tty (teletype) devices tty1tty6 as physical rooms in a building — you walk to the room (Ctrl+Alt+F1) and log in at its door. pts (pseudo-terminal slave) devices pts/0, pts/1 are phone extensions — remote SSH callers dial in and get an extension without occupying a physical room. who is the receptionist's board listing who is in which room or on which extension and since when. wall is the PA system that broadcasts to every room and extension at once. Switching virtual consoles is walking between rooms; your SSH pts call stays connected regardless of which room you stand in — until the network drops. Break point: tty persists locally even if the network hiccups; pts is tied to the network and may drop if ifconfig IP changes or SSH disconnects, which the demo experienced and recovered from by staying on local tty consoles.

Formalize — who, tty/pts, wall, and virtual consoles

who — session census: Lists each logged-in user, device, and login time. Example from demo:

ubuntu  tty7   2022-11-06 09:00 (:0)      ← graphical desktop (see below)
bits1   tty1   2022-11-06 09:05
bits2   tty2   2022-11-06 09:06
bits3   tty3   2022-11-06 09:07
bits4   tty5   2022-11-06 09:08
bits5   tty?   ...
ubuntu  pts/0  2022-11-06 09:00 (192.168.1.10)  ← SSH from instructor laptop

pts/0 field in parentheses shows the remote origin for network logins.

tty vs pts:

  • tty1tty6 — local virtual consoles tied to the machine's keyboard/display. Each is an independent kernel terminal device (/dev/tty1 etc.).
  • pts/*pseudo-terminal slave devices for remote logins (SSH, telnet, terminal emulators). Each SSH connection allocates a new pts number; the instructor's VM connection appeared as pts/0 or pts/1 distinct from tty entries.
  • Auxiliary probes: whoami prints effective user for current shell, id prints UID/GID/groups, tty (no args) prints the device of the current shell (/dev/pts/0 when on SSH) — handy to know which line you are on before broadcasting.

wall — write to all: Broadcasts a message to every logged-in terminal immediately, without a recipient list. Session demo:

wall
Hello everyone. System shutdown in 5 minutes. Save your work!
# Ctrl-D to send EOF

After Enter/Ctrl-D, the banner:

Broadcast Message from ubuntu@vm (pts/0) at 10:20 ...
Hello everyone system shutdown in 5 minutes. Save your work!

appears inline on tty1, tty2, tty3, and the originating pts, interleaved with each recipient's current session. wall requires no who parsing; it delivers to all who entries by design. Historically wall needs superuser or mesg y permission on terminals; modern wall may be setgid tty.

Linux virtual consoles and the graphical session:

  • Ctrl+Alt+F1 through Ctrl+Alt+F6 switch to virtual consoles tty1tty6, each presenting a text login: prompt. Demo logged bits1 on F1, bits2 on F2, bits3 on F3, bits4 on F4, confirmed via who.
  • Ctrl+Alt+F7 (on many distros F1/F2 is graphical; in this VM F7 was desktop) returns to the graphical desktop, reported as tty7 in who.
  • Network hiccup handling: a lost ifconfig IP after a service hiccup dropped the pts SSH session, but local tty sessions persisted; the instructor stayed on tty consoles and retried ifconfig/service networking restart, illustrating the resilience split.

Supplementary commands reachable from any virtual console: exit leaves a console, clear refreshes display, passwd changes password, ulimit shows/limits per-user resources (ulimit -f max file size), all without affecting parallel sessions.

Process lineage note (R1 Ch 13): each tty's gettyloginshell chain is respawned by init per /etc/inittab respawn lines, so logging out returns to login: on that tty without affecting other tty/pts sessions.

Worked Example — mapping who to devices and broadcasting

Step 1 — populate consoles:

# From graphical terminal (tty7), switch: Ctrl+Alt+F1, login bits1
# Ctrl+Alt+F2, login bits2
# Ctrl+Alt+F3, login bits3
# Ctrl+Alt+F7 back to desktop
who
# bits1  tty1  ...
# bits2  tty2  ...
# bits3  tty3  ...
# ubuntu pts/0 ... (your SSH)
# ubuntu tty7 ... (graphical)
tty
# /dev/pts/0   ← current shell is SSH pseudo-terminal

Step 2 — broadcast:

wall <<'MSG'
Hello everyone. System shutdown in 5 minutes. Save your work!
MSG
# Observe on tty1: Broadcast Message from ubuntu@vm (pts/0) ...
# Same on tty2, tty3, pts/0

Step 3 — verify after broadcast:

who -a   # -a shows all, with idle and pid
who am i # owner of current terminal
id       # confirms uid/gid/groups for privilege context

Step 4 — network resilience:

ifconfig eth0 down    # simulated hiccup → pts/0 may freeze/disconnect
# Local tty1–tty3 remain responsive; re-enable with `sudo ifup eth0` from tty1

Sense-check: who after wall still lists all sessions; wall does not log anyone out, it only writes. To actually schedule shutdown, sudo shutdown -h +5 "Save work" would both broadcast and power down after 5 minutes.

Assumptions & Scope

Assumption: traditional SysV tty1tty6 + tty7 desktop mapping applies to the demo VM; modern systemd systems use logind and may place graphical on tty1/tty2 and text on tty3tty6 — check who and systemctl get-default.

Scope: wall is for urgent notices to interactive users; for daemon or non-interactive sessions use mail, logger, or shutdown's own wall. pts numbering is dynamic — pts/0 today may be pts/3 after reconnect.

Failure mode: mesg n on a terminal blocks wall/write to that terminal; users may have disabled messages. wall may require sudo or group tty membership depending on distribution.

Visual intuition: picture a building floor plan: six rooms labeled tty1tty6 along a corridor, one executive suite tty7 (desktop), and a phone switchboard with extensions pts/0pts/N for remote callers. who is a roster board at reception with pins for each occupant. wall is a loudspeaker icon with arrows to every room and extension. The x-axis is device, y is user; takeaway: sessions multiplex in space (room vs extension), not by disconnecting others.

Pitfalls

  • Confusing tty and pts. tty1 is local console; pts/0 is your remote SSH — they are different devices with different persistence. Diagnosing "who is logged in where" requires reading the second column of who, not just the name.
  • Expecting SSH to survive network loss like tty. A local tty survives; pts is network-bound and drops if IP changes (ifconfig demo). Have a local console fallback.
  • Forgetting to switch back to graphical. Staying on tty1 text console and thinking the desktop is frozen; Ctrl+Alt+F7 (or F1 on newer) returns to GUI.
  • Broadcast without purpose. wall interrupts every user; do not use for chat — use write user tty to message one user/terminal, or mail for non-urgent.
  • Misreading who's tty7. On this VM F7 is graphical, but on your laptop it may be tty1 or tty2; always verify with who rather than assuming.

Recap + Bridge

who shows who is where (tty local rooms vs pts remote extensions), wall PA-broadcasts to all, and Ctrl+Alt+F1F6/F7 multiplex independent logins plus a graphical desktop on one machine. Together with grep, privilege, users and groups, you now have the full loop: find text, manage who may see or change it, and reach those who are logged in.

Real-World & Domain Connection: In data-center ops, who/w plus last audits active and historic sessions; wall/shutdown warns before patch reboots; tty/pts diagnosis explains why SSH idle disconnects yet the console process continues; and who with id/grep pipelines (who | grep pts) separates remote from local risk during incident response.

12.10.1 who, tty, and pts — Local versus Remote Sessions

Linux tracks attachment: who reports each logged-in user, device, and login time — e.g., ubuntu and bits1bits4 on tty1tty5 plus ubuntu on pts/0/pts/1 via SSH. tty devices (tty1tty6) are local virtual consoles on the machine's keyboard/display; pts (pseudo-terminal slave) devices are remote logins such as the SSH connection from the instructor's laptop, appearing as pts/... distinct from tty. id and whoami complement who by showing effective user and group for the current shell.

12.10.2 Broadcasting with wall

wallwrite to all — broadcasts immediately to every logged-in terminal, demoed as:

wall
Hello everyone. System shutdown in 5 minutes. Save your work!
# Ctrl-D

After execution, Hello everyone system shutdown in 5 minutes. Save your work! appeared on tty1, tty2, tty3, and the originating terminal, bannered inline. It requires no recipient list; it delivers to all who entries by design.

12.10.3 Linux Virtual Consoles and the Graphical Session

A Linux host offers multiple simultaneous logins: Ctrl+Alt+F1F6 switch to virtual consoles tty1tty6 with text login prompts (demo logged bits1 on F1, bits2 on F2, bits3 on F3, bits4 on F4, who confirmed), and Ctrl+Alt+F7 (on some distros F1/F2 is graphical; in this VM F7 was desktop) returns to the graphical session reported as tty7 in who. This explains why who shows one human on several devices — each function-key console is an independent login. Network hiccups such as a lost ifconfig IP after a service hiccup were handled by persisting on local tty consoles and retrying network restart, illustrating that tty sessions persist while pts SSH sessions may drop if the network changes. Supplementary commands exit, clear, passwd, ulimit remain reachable from any virtual console.

Exam Guidance Summary

This section consolidates the examinable decisions and prediction skills emphasized across the lecture — the fine-grained distinctions that map directly to short-answer and output-prediction questions.

  • Assignment and team sizing (12.1): Three members per group is the design target and the evaluation assumption. Workload division, program allocation freezing, and preparation time all assume a trio. Smaller groups (at most one group of two permitted) must negotiate to reach three; delayed formation directly reduces time for the command-heavy scripts that follow. Expect program-allocation timing and team-formation rationale as short-answer prompts.
  • Shell programming through commands (12.1–12.2, 12.8–12.9): Shell scripting is assessed via purposeful use of system commands inside scripts, not isolated pattern printing. Be prepared to embed command results (grep counts, id output, /etc/passwd probes) inside loops/conditionals, and to justify why a particular command or option (grep -c vs grep -o | wc -l, adduser vs newusers) fits the file-processing step. Illustrative vs assessed tasks are a common conceptual contrast.
  • grep reporting options (12.3): The distinctions between -c (count lines, not words), -l (list file names only) vs default lines vs -h (suppress names) vs -o (only matching substring), and -w (whole word) vs substring vs -i (case-fold) vs -v (invert) are directly testable. Practice how -i, -v, -n compose and which flag dominates output shape (e.g., grep -i -v -c vs grep -c). The table-document and grep_file counts (3 → 5 with -i, 4 → 2 inverted with -i) are prototypical output-prediction exercises.
  • Regular-expression mechanics (12.4–12.6): Be ready to trace a given pattern on a provided file and explain why a variant differs:
  • .* vs *.* bridges spaces including empty; * repeats only the preceding atom (this*file fails to bridge space, this.*file succeeds).
  • \. vs . — literal dot vs wildcard (so 5.5\.5\..).
  • ^ outside vs ^ inside [^...] — anchor (beginning of line) vs negation (not in set), e.g., ^[0-9] (line starts digit, matches nothing in table.doc) vs [^0-9] (non-digit, matches all lines).
  • Character classes [A-Z], [A-Za-z]*, shorthands \d/\D/\w/\W/\s, and consecutive atoms [A-Z][A-Z][A-Z] controlling required length.
  • Escaping for literal \. when grepping decimals, IPs, versions.
  • Administrative distinctions (12.7–12.10): Conceptual comparisons and scenario tasks are frequent:
  • su (switch user, needs target password, su - takes login environment) vs sudo (one command as superuser, needs invoker password, sudo su idiom).
  • adduser/useradd (one bespoke account, interactive vs need -m/-s) vs newusers (batch of 26 from colon-separated file, sub-second) vs userdel (-r nuance, home preservation).
  • /etc/passwd seven fields and bash\$ vs nologin\$ interactive audit, /etc/group four fields and GID ≥500 rule.
  • Primary (default file group, one per user) vs secondary (supplementary, many) and the two-step sharing chgrp bits bits_doc + chmod g+w where membership alone is insufficient — the Anita/bits_doc scenario.
  • wall broadcast semantics (all who entries, no recipient list) vs write to a single terminal.
  • who device reading: tty1tty6 local virtual consoles (Ctrl+Alt+F1–F6) vs pts/0 SSH pseudo-terminals vs tty7 graphical (Ctrl+Alt+F7 on this VM) and why pts drops on network hiccup while tty persists.
  • Study advice: For each filter or regex problem, write the pattern atom by atom, state what each atom consumes or asserts, then simulate it line by line on the given file content — especially noting line-start/end and literal-dot adjacencies. For admin scenarios, draw the two-axis check: group membership (who) × file mode (rwx) — both must align.

Key Industry Applications

  • grep as triage for large text datasets (12.2–12.6): In production, grep is the first filter for log forensics (grep -i ERROR app.log | grep -v -i timeout), config auditing (grep "^PermitRootLogin" /etc/ssh/sshd_config, grep "^bits" /etc/passwd, grep "bash\$" /etc/passwd vs grep "nologin\$" to separate interactive from system accounts), and pipeline shaping (ps aux | grep apache, ls | grep -v temp, cat mail | grep "^From"). Literal-dot escaping (grep "192\.168\." auth.log for local IPs, grep "5\." data.csv for decimals, grep "1\.2\." CHANGELOG for version strings) prevents wildcard over-matching where a dot must be literal, and anchors ^/\$ validate line-oriented formats (e.g., grep "^ERROR:.*\$"). Combined with grep -c/-l/-o and downstream sort | uniq -c | awk, it turns thousands of lines into actionable counts before heavier tools are invoked.
  • System administration with superuser delegation (12.7): The su/sudo model governs all privileged operations in operations teams — restarting services (sudo systemctl restart nginx), mounting datasets (sudo mount), and changing clocks or passwords. The discipline taught (regular user by default, sudo one command at a time, exit promptly, verify with id/whoami) is the production safety habit that prevents rm -rf catastrophes noted in administrative texts. Auditing with who/id/grep on /etc/passwd and /etc/group forms the basic access-review loop for compliance.
  • Batch user and group provisioning for team access (12.8–12.9): Semester-start cohort creation (newusers from an HR export of 26 2021... accounts in one sudo newusers Users run, verified with grep -c "^2021" /etc/passwd) is the standard alternative to error-prone adduser loops, and the same pattern scales to enterprise onboarding and lab fleet setup. Managing groups (groupadd -g 1016 bits, gpasswd -a anita bits, chgrp bits /srv/shared, chmod g+w / chmod 2775 with setgid so new files inherit the shared group) implements team-based access for shared directories, CI artifacts, and submission drop-boxes. The Anita/bits_doc trap — membership without g+w is read-only — is the canonical lesson that group membership (who) must be paired with file mode (what) and verified via ls -l and id.
  • Session and communication awareness (12.10): Interpreting who (tty1tty6 local virtual consoles on Ctrl+Alt+F1F6 vs pts/0 remote SSH pseudo-terminals vs tty7 graphical) lets on-call engineers know whether a suspected session is local or remote during incident response (who | grep pts to isolate SSH users). Broadcasting urgent notices with wall before maintenance (wall <<'MSG' System shutdown in 5 minutes ...) remains the quickest in-band warning to all interactive sessions before shutdown. Understanding that local tty sessions persist while pts SSH sessions drop on network/IP changes explains why console-based recovery (Ctrl+Alt+F1) is the fallback when remote access fails, and how init respawns getty/login/shell per /etc/inittab keeps other consoles alive.
  • Glue-role synthesis: The lecture's overarching message — shell scripts are practical when they orchestrate filters like grep for meaningful administration — is the daily reality of platform engineering: a five-line script that greps logs for error signatures, counts with -c, and mails or walls a report replaces hours of manual inspection, and the same ^/\$/\. pattern skills transfer unchanged to validating data files, auditing accounts, and gating automated pipelines.

SP Lecture 12 notes · Shell Filters and System Administration

Systems Programming· postgraduate· 2026-08-20

Sections Breakdown

112.1 Shell Scripts and Assignment Structure

Shell scripts glue commands with loops/conditionals; assignment expects practical file and admin automation for teams of three, not pattern printing.

212.2 The grep Filter — Purpose and Syntax

grep is a selective filter (g/RE/p) that scans lines for a literal or regex pattern and prints matches; syntax grep [options] pattern [file ...] with exit codes 0/1/2.

312.3 grep Options for Output Control

Options reshape grep reporting: -c counts lines, -l lists files, -o isolates matches, -h/-n control labels, -i/-w/-v control match strictness, -F/-E/-f choose pattern language.

412.4 Regular Expression Building Blocks

Atoms: . one any, * zero-or-more of previous (so .* any string), [...] one of set with ranges; star vs dot-star is the key trap; classes control length.

512.5 Anchors, Escaping, and Word Matching

Anchors ^/$ assert line edges zero-width; \ literalizes (. -> \.); -w enforces whole-word; -E/-F choose extended vs fixed; ^ placement outside vs inside [] flips anchor vs negation.

612.6 Worked grep Examples — File Searches and Table Document

Hands-on tracing on grep_file/demo_file and table.doc: counts, anchors, literal dots, -w, and Q&A on star vs dot-star, dot-dot analogy, caret placement.

712.7 Superuser Status and Privileged Operations

su switches user (su - takes login env), sudo borrows superuser for one command; prompt and id confirm; gated demo date -s; superuser scope covers files, processes, passwords, clock, services.

812.8 User Management and Bulk Creation

adduser/useradd for one account, /etc/passwd seven fields, newusers bulk 26 accounts from colon file in one command, verified with grep ^2021.

912.9 Groups and Permission Delegation

Primary = default uniform, secondary = club badges; /etc/group 4 fields, GID >=500; gpasswd/adduser manage; sharing needs chgrp bits + chmod g+w; membership without w stays read-only.

1012.10 Session Awareness, Communication, and Workspaces

who lists tty (local rooms tty1-6) vs pts (SSH extensions), wall PA broadcasts to all, Ctrl+Alt+F1-6 virtual consoles and F7 graphical multiplex sessions; local tty persists when pts drops on network hiccup.

11Exam Guidance Summary

Consolidated exam focus: trio team sizing, command-driven scripting, grep option and regex tracing, admin distinctions primary/secondary, chgrp+chmod, wall/tty/pts.

12Key Industry Applications

grep triage, superuser ops, batch provisioning and group sharing, session awareness as production patterns.

Postgraduate students in Systems Programming

Exam Revision Notes

Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.

Shell Scripts and Assignment Structure

Must-know: Scripts are assessed on purposeful command use inside loops, not star patterns; teams of three, one group may have two.

Top pitfall: Mistaking star-pattern illustration for the assessed objective; learning scripting without command fluency.

Self-check: Why does fluency on the command line transfer directly into scripts?

Connects to: 12.2, 12.3

The grep Filter — Purpose and Syntax

Must-know: grep selects lines containing pattern without modifying files; pattern may be literal or regex; exit 0 found, 1 not found, 2 file error.

grep [options] pattern [file ...]

Top pitfall: Forgetting quotes on patterns with spaces/shell metas; expecting in-place edit; directory without -r gives Is a directory.

Self-check: What exit status does grep return when the pattern is not found vs file not found?

Connects to: 12.3, 12.4

grep Options for Output Control

Must-know: -c counts lines not words; -l lists filenames; -w whole word vs substring; -i folds case; -v inverts; they compose.

grep -c/-l/-o/-h/-n/-i/-w/-v/-e/-F/-f/-E pattern [file]

Top pitfall: Confusing -c line count with occurrence count; expecting -l to show lines; -i inflating matches; -w substring surprise.

Self-check: If grep this finds 3 lines, what do grep -c this, grep -i -v -c this, and grep -o this | wc -l each report and why?

Connects to: 12.4, 12.5

Regular Expression Building Blocks

Must-know: . one any; * repeats preceding atom; .* bridges spaces; [A-Z] one capital; [A-Z]* zero-or-more; need [A-Za-z][A-Za-z]* for one-or-more.

Top pitfall: Reading * as any string; forgetting .* matches empty; confusing single char class with string.

Self-check: Why does this*file not bridge a space while this.*file does?

Connects to: 12.5, 12.6

Anchors, Escaping, and Word Matching

Must-know: ^ outside anchors start, ^ inside leading [^...] negates; $ anchors end; \. literal dot; -w whole word; -E adds +?|().

^SAP , bash$, 5\. , 5\.., grep -w North, grep -E pattern

Top pitfall: ^[0-9] vs [^0-9] confusion; 5. vs 5\. vs 5\..; basic vs extended +|() surprise.

Self-check: Trace ^[0-9] vs [^0-9] on table.doc — which prints nothing, which prints all, and why?

Connects to: 12.6, 12.8

Worked grep Examples — File Searches and Table Document

Must-know: Trace counts 3 vs 5 with -i; ^this vs file$ vs file\. vs ^this.*file\.$; 5\.. hits column four not bare 5; -w excludes Northwest.

grep -i -c this; grep ^this.*file\.$; grep 5\.. ; grep -w North ; grep ^[0-9] vs [^0-9]

Top pitfall: Counting lines vs occurrences; star vs dot-star; dot-dot as parent dir; caret outside vs inside; trailing dot in file$ vs file\.

Self-check: Why does grep -w 5\. on table.doc yield no output while grep 5\. matches lines?

Connects to: 12.5, 12.7

Superuser Status and Privileged Operations

Must-know: su needs target password, sudo needs invoker password; su - changes HOME/USER; id uid 0 = root; date -s gated; read vs write privilege.

su ; su - ; sudo command ; sudo su ; date -s 2022-11-05

Top pitfall: Trying sudo on unganted host; confusing passwords; staying root and damaging system; su without - retaining wrong env.

Self-check: What prompts differ between su and sudo and what does id show after each?

Connects to: 12.8, 12.9

User Management and Bulk Creation

Must-know: /etc/passwd 7 fields (user:x:UID:GID:GECOS:home:shell); adduser interactive vs useradd needs -m -s; newusers Users bulk; userdel -r nuance.

sudo adduser bits1 ; grep ^bits /etc/passwd ; sudo newusers Users ; grep -c ^2021 /etc/passwd

Top pitfall: Editing /etc/passwd by hand; forgetting -m/-s on useradd; malformed colon file; assuming userdel cleans home.

Self-check: When would you choose newusers over adduser and how do you verify 26 accounts were created?

Connects to: 12.9, 12.5

Groups and Permission Delegation

Must-know: Primary one per user, default file group; secondary many; /etc/group 4 fields; groupadd GID>=500; sharing = chgrp + chmod g+w; ls -l shows rwx triplet.

sudo groupadd -g 1016 bits ; sudo gpasswd -a anita bits ; sudo chgrp bits bits_doc ; sudo chmod g+w bits_doc

Top pitfall: Membership without g+w still denied; chgrp does not change user primary; manual /etc/group edit; forgetting re-login for new groups.

Self-check: Why does Anita added to bits still get Permission denied on bits_doc after chgrp, and what chmod fixes it?

Connects to: 12.8, 12.10

Session Awareness, Communication, and Workspaces

Must-know: tty1-6 local consoles vs pts SSH slaves; who second column shows device; wall broadcasts to all; Ctrl+Alt+F7 returns to desktop; pts drops on network loss.

who ; wall ; Ctrl+Alt+F1-6 / F7

Top pitfall: Confusing tty vs pts; expecting SSH to survive network like tty; thinking desktop frozen when on tty1.

Self-check: In who output, how do you distinguish a local console login from an SSH login and what broadcast reaches both?

Connects to: 12.7, 12.8

Exam Guidance Summary

Must-know: Trace grep patterns atom by atom on given file; two-axis check membership x mode.

Top pitfall:

Self-check:

Connects to: -

Was this lecture useful?

Loading comments…
🤖

BitsNotes AI Assistant

Subject Notes Assistant

Configure AI Chat

Choose how to access the chatbot
Have your own API key?

Switch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.

🔑 Enter API key above to fetch live models from provider, or enter model name manually.
OpenAI-Compatible API Support

Choose any provider preset (Gemini, DeepSeek, Kimi, GLM, MiniMax, Qwen, OpenAI, Groq, Ollama, etc.) or enter a custom endpoint URL.

Security & Privacy First

Your API key is sent directly from your browser to your specified provider. BitsNotes servers never store or see your key.