Classroom Public page

Lab 9.1: Bash Script

339 words

~75 minutes. Write analyze.sh: a script that takes a filename argument, checks it exists, and prints a line/word/character summary.


Goal: write a well-structured bash script with argument checking, file validation, and formatted output. The script must handle errors gracefully.

Estimated time: 75 minutes

Prerequisites: Week 9 lecture (variables, conditionals, positional parameters, wc command)


Part A: Minimal version

Create analyze.sh in ~/fnd-101/lab-9-1/:

mkdir -p ~/fnd-101/lab-9-1
cd ~/fnd-101/lab-9-1

Write the following script:

#!/bin/bash
# analyze.sh -- file statistics summary

if [ "$#" -eq 0 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

FILE="$1"

if [ ! -f "$FILE" ]; then
    echo "Error: '$FILE' not found or is not a regular file"
    exit 1
fi

LINES=$(wc -l < "$FILE")
WORDS=$(wc -w < "$FILE")
CHARS=$(wc -c < "$FILE")

echo "File: $FILE"
echo "Lines: $LINES"
echo "Words: $WORDS"
echo "Bytes: $CHARS"

Make it executable:

chmod +x analyze.sh

Part B: Test on two different files

Run analyze.sh on at least two different files:

./analyze.sh sample/logs/access.log
./analyze.sh sample/docs/annual-report.txt

Record the output for both runs in your notes.


Part C: Test error handling

  1. Run the script with no arguments: ./analyze.sh. Does it print the usage message and exit?

  2. Run it on a file that does not exist: ./analyze.sh nonexistent.txt. Does it print the error message?

  3. Run it on a directory: ./analyze.sh sample/. What happens? (A directory is not a regular file, so [ ! -f "$FILE" ] should catch it.)


Part D: Add an ERROR count feature

Extend the script to count lines containing the string "ERROR" (case-insensitive) when the --count-errors flag is passed as the second argument:

if [ "$2" = "--count-errors" ]; then
    ERRORS=$(grep -ic "error" "$FILE")
    echo "Lines with 'error' (case-insensitive): $ERRORS"
fi

Test:

./analyze.sh sample/logs/error.log --count-errors

(Create sample/logs/error.log first if you have not already, from Lab 8.2.)


Part E: Make the output nicer

Add a separator line and a file size:

SIZE=$(du -h "$FILE" | cut -f1)
echo "=========================="
echo "File: $FILE"
echo "Size: $SIZE"
echo "Lines: $LINES"
echo "Words: $WORDS"
echo "Bytes: $CHARS"
echo "=========================="

(du -h gives a human-readable size like "4.0K"; cut -f1 takes just the size field.)


Expected output / artifact

lab-9-1/analyze.sh (the completed script, with all extensions from Parts A-E)

Also: lab-9-1/test-output.txt showing the output of running the script on 3 different inputs (good file, missing file, directory).

git add lab-9-1/
git commit -m "lab-9-1: analyze.sh bash script with error handling"

Common pitfalls

  • Spaces around = in test: [ "$#" -eq 0 ] is correct; [$# -eq 0] without spaces will fail
  • Quoting $FILE: always quote variables that might contain spaces: "$FILE" not $FILE. A filename like "my file.txt" will break an unquoted command.
  • wc -l < "$FILE" vs wc -l "$FILE": the < redirection removes the filename from wc's output. Without <, wc prints " 5 filename" instead of just " 5". Use the redirection for cleaner output.
  • exit codes: exit 1 signals failure to the caller; exit 0 signals success. Scripts that exit 0 on error confuse other programs that check exit codes.

Stretch (optional)

Add a --help flag that prints a multi-line usage description:

if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
    cat << 'EOF'
analyze.sh -- file statistics

Usage: analyze.sh <filename> [--count-errors]

Options:
  --count-errors    also count lines containing 'error' (case-insensitive)
  --help, -h        show this help
EOF
    exit 0
fi

Lab 9.1 v0.1.