grep1-4 Exercise 3: A Script That Counts, and Tells Silence from Failure ========================================================================= (Run from an empty folder. The first block makes the two practice files from the chapter; your own files can skip it.) cat > app.log <<'EOF' 09:00:01 INFO service starting 09:00:02 INFO loading config 09:00:03 WARN config key 'timeout' missing, using default 09:00:04 INFO connecting to database 09:00:09 ERROR database connection refused 09:00:10 INFO retrying in 5s 09:00:15 ERROR database connection refused 09:00:16 ERROR giving up after 2 attempts 09:00:17 INFO switching to read-only mode 09:00:20 INFO listening on port 8080 09:05:00 INFO health check ok 09:10:00 WARN slow response: 2300 ms 09:10:30 INFO health check ok 09:15:00 ERROR disk usage 95%, ERROR threshold 90% 09:15:01 INFO alert sent 09:20:00 INFO health check ok EOF cat > access.log <<'EOF' 10.0.0.5 GET /index.html 200 10.0.0.7 GET /about.html 200 10.0.0.5 POST /login 302 10.0.0.9 GET /missing 404 10.0.0.5 GET /index.html 200 10.0.0.7 GET /index.html 200 10.0.0.5 GET /dashboard 200 10.0.0.9 GET /missing 404 10.0.0.7 GET /logout 302 EOF # --- the exercise --- cat > errcount.sh <<'EOF' #!/bin/bash # usage: errcount.sh FILE n=$(grep -sc ERROR "$1") case $? in 0) echo "$1: $n line(s) with ERROR" ;; 1) echo "$1: no ERROR lines" ;; *) echo "errcount: cannot read $1" >&2; exit 2 ;; esac EOF bash errcount.sh app.log; echo "exit status: $?" bash errcount.sh access.log; echo "exit status: $?" bash errcount.sh nosuchfile; echo "exit status: $?" echo echo "two busiest clients in access.log:" grep -oE '^[0-9.]+' access.log | sort | uniq -c | sort -rn | head -2 Output: app.log: 4 line(s) with ERROR exit status: 0 access.log: no ERROR lines exit status: 0 errcount: cannot read nosuchfile exit status: 2 two busiest clients in access.log: 4 10.0.0.5 3 10.0.0.7 WHY THIS WORKS AS AN ANSWER --------------------------- -s hides grep's own message about a missing file so the script can print one of its own; -c prints the count. The exit status is what separates the cases: 0 (there were matching lines, and n holds the count), 1 (nothing matched: -c printed 0 and the script says so plainly), and 2 (the file could not be read, in which case -c printed nothing at all, and the script says so on standard error and exits with 2 itself). The status has to be read straight after the command substitution: $? is the status of n=$(...), which is grep's own. Putting any command in between would overwrite it. The last line is the chapter's counting recipe again, with head -2 to keep the two busiest.