Find the relevant records in a log, compare configurations and build an incident summary that another engineer can reproduce.
Series: Previous: files · Part 2 of 6 · Next: permissions
Applies to: Bash, GNU text utilities, GNU find, GNU sed and a POSIX-compatible awk. Output from the fixture below is deterministic; service-log output is illustrative.
The subsystem: streams and filesystem traversal
Most tools here run in user space. They read files through filesystem interfaces and transform bytes or records. find walks directory entries; it is not a text search engine. grep searches file contents. Pipelines connect standard streams so you can reduce a large input to useful evidence.
In enterprise work, this helps with incident triage, configuration review and batch reporting. Start with a small fixture, not a confidential production log:
lab=$(mktemp -d)
printf 'INFO start\nERROR db timeout\nWARN retry\nERROR cache timeout\n' > "$lab/app.log"
printf 'workers=2\n' > "$lab/before.conf"
printf 'workers=4\n' > "$lab/after.conf"
cat: display or concatenate complete inputs
cat -- "$lab/before.conf" "$lab/after.conf"
Expected output is workers=2 followed by workers=4, on separate lines. This is useful for short text fixtures. A multi-gigabyte log will flood your terminal; use a pager or select records first. Treat untrusted terminal output cautiously because control characters can affect its display.
less: browse without loading the whole story into the terminal
less -- "$lab/app.log"
Press /, type ERROR, then Enter to search; n moves to the next match and q quits. less is interactive and normally belongs in a human investigation rather than an unattended script. It may not be installed in a minimal image. Its displayed screen is not conventional command output to pipe elsewhere.
head and tail: look at the beginning or end
head -n 2 -- "$lab/app.log"
tail -n 2 -- "$lab/app.log"
The first command shows the start and first error; the second shows the warning and last error. Explicit counts make a runbook clearer than relying on defaults. To follow an authorized application log, use tail -F /path/to/app.log and Ctrl-C to stop. GNU -F follows the filename and retries after rotation; -f usually follows the open file descriptor and can keep watching the old file.
grep: select matching records
grep -n -F -- 'ERROR' "$lab/app.log"
Expected output:
2:ERROR db timeout
4:ERROR cache timeout
-F treats the pattern as a fixed string, while -n adds line numbers. Use -E when you intend an extended regular expression. An exit status of 1 means no match, not a broken file. Check permissions and status 2 before reporting “there were no errors.”
find: locate files by their metadata or name
find "$lab" -maxdepth 1 -type f -name '*.conf' -print
This lists both configuration files. Quote the pattern so find, rather than the shell, evaluates it for each entry. For bulk operations use -exec command {} + or a NUL-delimited interface such as -print0; do not split the output on whitespace. Keep searches bounded to an approved directory, particularly on busy shared filesystems.
diff: make configuration changes visible
diff -u -- "$lab/before.conf" "$lab/after.conf"
The hunk includes -workers=2 and +workers=4; file headers and timestamps vary. Status 0 means identical, 1 means differences, and a larger status means trouble. That distinction matters under set -e: expected differences should be handled explicitly rather than aborting an otherwise successful report.
wc: count records, words or bytes
wc -l < "$lab/app.log"
wc -c < "$lab/app.log"
For the fixture, the first command prints 4. -l counts newline characters, so an unterminated final record affects the result. -c counts bytes; -m counts characters according to the locale. They are not interchangeable for Unicode logs.
sed: select or transform a stream
sed -n '2,3p' "$lab/app.log"
sed 's/timeout/deadline exceeded/g' "$lab/app.log"
The first prints records two and three. The second prints a transformed copy while leaving the file unchanged. That is a useful way to preview a textual replacement. sed -i would edit files; review the preview and retain a recovery copy before using it on a real configuration. A regular expression is not a reliable parser for every data format.
awk: work with fields and aggregate records
awk '$1 == "ERROR" {count++} END {printf "Errors: %d\n", count+0}' "$lab/app.log"
Expected output is Errors: 2. Awk splits each input line into whitespace-separated fields by default. Here the first field is the level. For comma-separated data, -F, can handle a simple delimiter-separated fixture, but quoted CSV requires a proper CSV parser. Know the input format before counting.
Practical script: an incident handover report
You receive a small log and two configuration snapshots. Produce a summary with the last few records, error lines and configuration differences. Save as log-handover.sh, then run bash log-handover.sh "$lab/app.log" "$lab/before.conf" "$lab/after.conf" in the same shell as the fixture.
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
if (( $# != 3 )); then
printf 'Usage: bash log-handover.sh LOG BEFORE AFTER\n' >&2
exit 2
fi
for input in "$@"; do
[[ -f $input && -r $input ]] || { printf 'Unreadable input\n' >&2; exit 2; }
done
printf 'Newline-terminated records: '
wc -l < "$1"
printf '\nOpening records:\n'
head -n 2 -- "$1"
printf '\nRecent records:\n'
tail -n 3 -- "$1"
printf '\nError records:\n'
if grep -n -F -- ERROR "$1"; then
:
else
status=$?
if (( status != 1 )); then exit "$status"; fi
printf 'No matching records.\n'
fi
awk '$1 == "ERROR" {n++} END {printf "Error total: %d\n", n+0}' "$1"
printf '\nConfiguration differences:\n'
if diff -u -- "$2" "$3"; then
printf 'Configurations match.\n'
else
status=$?
if (( status != 1 )); then exit "$status"; fi
fi
The report should contain two errors and the worker-count change. It neither edits the inputs nor sends the report anywhere. Redact secrets before attaching it to a ticket. A live log can change while you read it; use an approved snapshot when exact counts matter. The script supports the simple text format above, not arbitrary structured logs or compressed inputs.
References
GNU grep, GNU find, GNU diffutils, GNU sed, GNU awk, less project.