sysadmin · May 22, 2026 · 13 min read

Three reports out of /etc/passwd.
grep, awk, sed, cut, sort, uniq.

A quarterly access audit needs the regular users, the top login shells, and the locked accounts pulled out of the system files, plus an in-place edit to a config that cannot be broken.

// what we’re getting into
  1. The ticket
  2. Concept review. One tool per job.
  3. Report one. Regular users.
  4. Report two. The top three shells.
  5. Report three. Locked accounts.
  6. The in-place edit, done safely
  7. grep power moves
  8. Exam questions
  9. Final checklist: confirm everything works

The ticket: auditor1 needs three reports out of /etc/passwd for the Q2 access audit. Every regular user with UID and shell, the three most common login shells with counts, and every account whose password is locked. Save them in /srv/audit_reports/. Also, /etc/motd.bak has the wrong company name throughout, and every “OldCorp” needs to become “Server1 Corp” in place, without breaking the file.

The real problem: nothing here is hard once you use the right tool for each slice. The exam loves asking for one specific column of a system file, so this is core muscle.

What we are doing: grep, awk, sed, cut, sort, and uniq on real system files, plus a safe in-place edit.

The ticket

bash
id auditor1     # UID 3502
ls -ld /srv/audit_reports 2>/dev/null || sudo mkdir -p /srv/audit_reports

Concept review. One tool per job.

Read the manual pages, then match the tool to the task. /etc/passwd is colon delimited, which is why the field tools shine here.

bash
man grep
man awk
man sed
man cut
the toolkit
grep   filter the lines that match a pattern
awk    field-based processing, conditionals, math, formatting
sed    stream editing, find and replace, in-place edits
cut    pull columns out by delimiter
sort   order lines, alphabetically or numerically
uniq   collapse or count adjacent duplicate lines (needs sorted input)

The field layout you are slicing: user:x:UID:GID:comment:home:shell.

Report one. Regular users.

Real users start at UID 1000. Filter on the third field and print the fields you want, formatted so the auditor can read it.

bash
# see it first
awk -F: '$3 >= 1000 {print $1, $3, $7}' /etc/passwd

# aligned columns, saved to the report
sudo mkdir -p /srv/audit_reports
sudo chown admin:auditors /srv/audit_reports
sudo chmod 770 /srv/audit_reports
awk -F: '$3 >= 1000 {printf "%-20s %-6s %s\n", $1, $3, $7}' /etc/passwd \
  | sudo tee /srv/audit_reports/regular_users.txt
sudo chgrp auditors /srv/audit_reports/regular_users.txt

-F: sets the delimiter, $3 is the UID, $1 the name, $7 the shell. printf keeps the columns lined up.

Report two. The top three shells.

Build this one pipe at a time so you can watch it take shape. The order is deliberate, because uniq -c only collapses adjacent duplicates and needs sorted input.

bash
cut -d: -f7 /etc/passwd                          # every shell
cut -d: -f7 /etc/passwd | sort                   # sorted so duplicates sit together
cut -d: -f7 /etc/passwd | sort | uniq -c         # counted
cut -d: -f7 /etc/passwd | sort | uniq -c | sort -rn | head -3   # top three

# save it
cut -d: -f7 /etc/passwd | sort | uniq -c | sort -rn | head -3 \
  | sudo tee /srv/audit_reports/top_shells.txt
sudo chgrp auditors /srv/audit_reports/top_shells.txt

Report three. Locked accounts.

The password hash lives in /etc/shadow, not /etc/passwd, and a locked account has a leading ! on the hash. This needs sudo to read.

bash
sudo awk -F: '$2 ~ /^!/ {print $1}' /etc/shadow \
  | sudo tee /srv/audit_reports/locked_accounts.txt
sudo chgrp auditors /srv/audit_reports/locked_accounts.txt
sudo chmod 640 /srv/audit_reports/locked_accounts.txt   # shadow-derived, keep it tight

$2 ~ /^!/ is an awk regex match: field two begins with !. Because it comes from shadow, lock the report down to the auditors group.

The in-place edit, done safely

sed -i edits the file directly, which is exactly what is asked, and exactly what bites people who skip a backup. Always copy first.

bash
# build the test file
sudo bash -c 'cat > /etc/motd.bak << EOF
Welcome to OldCorp Production Server
For support, contact OldCorp IT
This system is property of OldCorp.
EOF'

# back up, then replace globally, in place, then diff to confirm
sudo cp /etc/motd.bak /etc/motd.bak.backup
sudo sed -i 's/OldCorp/Server1 Corp/g' /etc/motd.bak
sudo diff /etc/motd.bak.backup /etc/motd.bak

s/old/new/g substitutes every match on each line, and the diff against the backup proves you changed only what you meant to.

grep power moves

A handful of grep flags that come up constantly.

bash
grep -i "root" /etc/passwd          # case insensitive
grep -E "bash|zsh" /etc/passwd      # extended regex, alternatives
grep -v "nologin" /etc/passwd        # invert, lines that do NOT match
sudo grep -r "PermitRootLogin" /etc/ssh/   # recursive across files
grep -n "root" /etc/passwd           # show line numbers
grep -c "bash" /etc/passwd           # count matching lines

Exam questions

Write the command first.

Q1. Print username and home directory side by side for all regular users (UID 1000 and up).

Q2. List all users whose shell is /bin/bash, showing username and UID, sorted numerically by UID.

Q3. Replace every OldCorp with Server1 Corp in /etc/motd.bak in place, showing the file before and after.

Answers.

bash
# A1
awk -F: '$3 >= 1000 {print $1, $6}' /etc/passwd
# A2
awk -F: '$7 == "/bin/bash" {print $1, $3}' /etc/passwd | sort -k2 -n
# A3
sudo cat /etc/motd.bak
sudo sed -i 's/OldCorp/Server1 Corp/g' /etc/motd.bak
sudo cat /etc/motd.bak

Final checklist: confirm everything works

If every check passes, the ticket is done.

bash
# 1. all three reports exist
ls -l /srv/audit_reports/regular_users.txt /srv/audit_reports/top_shells.txt /srv/audit_reports/locked_accounts.txt

# 2. no OldCorp left in the motd
grep -c OldCorp /etc/motd.bak    # expect 0

# 3. locked report is auditors-only (640)
ls -l /srv/audit_reports/locked_accounts.txt

# 4. all three exam commands written from scratch
# 5. tracker entry checked off

Reply to auditor1: all three reports are in /srv/audit_reports/, the locked-accounts one restricted to the auditors group, and /etc/motd.bak is updated, backed up before the edit and verified with diff.

next post
Two shell scripts with clean exit codes and an audit trail