
Build a small Bash script that counts newline characters in a folder of text files, then use its output and exit status to check what happened. The example handles spaces in filenames and gives you a safe place to practice arguments, quoting, loops and error handling.
You need Bash and the wc utility, available together in many Unix environments and Git Bash on Windows. Run the commands in Bash; PowerShell uses different syntax. The download was checked with Bash 5.2.37. It reads the sample files and prints a report without changing them.
Start with a working folder
Download the Bash practice kit, extract it into a new folder, and open a Bash terminal in the extracted sample directory. Run:
bash count-lines.sh notes
The supplied fictional files contain two newline characters in alpha.txt and one in two words.txt. With the ordinary locale ordering, the output is:
file newlines
alpha.txt 2
two\ words.txt 1
The actual separator is a tab. Bash's %q formatting displays a space as \ so the filename remains unambiguous; the backslash is presentation, not part of that filename. A final line without a terminating newline contributes zero to wc -l. Decide whether your task needs newline counts or logical records before adapting this example.
Read the script from input to result
#!/usr/bin/env bash
# Yenra educational example. Counts newline characters in visible *.txt files.
# Usage: bash count-lines.sh DIRECTORY
# https://yenra.com/bash-shell-script-programming/
if (( $# != 1 )); then
printf 'Usage: bash count-lines.sh DIRECTORY\n' >&2
exit 2
fi
dir=$1
if [[ ! -d $dir || ! -r $dir || ! -x $dir ]]; then
printf 'Directory must exist and be readable/searchable: %s\n' "$dir" >&2
exit 2
fi
shopt -s nullglob
status=0
printf 'file\tnewlines\n'
for file in "$dir"/*.txt; do
[[ -f $file && ! -L $file ]] || continue
if count=$(wc -l < "$file"); then
count=${count//[[:space:]]/}
printf '%q\t%s\n' "${file##*/}" "$count"
else
printf 'Could not read: %s\n' "$file" >&2
status=1
fi
done
exit "$status"
The first guard requires exactly one argument. The second checks that it identifies a readable, searchable directory. Both use status 2 to describe incorrect invocation. That convention belongs to this script; other tools can assign different meanings to their nonzero statuses.
"$dir"/*.txt protects the directory argument while allowing the wildcard to expand. Quoting the entire expression as "$dir/*.txt" would make the asterisk literal. nullglob makes a pattern with no matches expand to an empty list, so an empty directory prints only the header. The loop skips symbolic links and entries that are not regular files. Hidden names and nested directories are outside this example's scope.
The redirection in wc -l < "$file" supplies file contents on standard input. It also avoids passing a filename beginning with a dash as a wc option. The assignment appears in an if, making its success or failure an explicit branch. A read failure sets the final status to 1 while allowing the loop to examine later files.
Bash-specific features here include [[ ... ]], arithmetic (( ... )), shopt, the substitution that removes whitespace from the count, and %q. Invoke this file with Bash. The GNU Bash manual documents these features; the installed commands help printf and help shopt give local reference material. For the underlying quote rules, see the POSIX shell language specification.
Check failure as well as success
Run each command separately and print its status immediately afterward:
bash count-lines.sh missing-folder
printf 'exit=%s\n' "$?"
The expected result is a directory diagnostic on standard error and exit=2. Any command placed between those two lines would replace the status you meant to inspect.
| Exercise | Expected result | What it checks |
|---|---|---|
Supplied notes folder |
Counts 2 and 1; exit 0 | Normal path and a filename containing a space |
| Existing empty folder | Header only; exit 0 | Wildcard with no matches |
| Missing argument | Usage message; exit 2 | Argument validation |
| A text file with no final newline | Count includes only actual newlines | Definition of the measurement |
Before running a changed script, use bash -n count-lines.sh to check syntax. To follow execution, use bash -x count-lines.sh notes with these harmless fixtures. Tracing prints expanded values, so use fabricated data when investigating scripts that normally handle credentials or private records. A syntax pass establishes that Bash can parse the file; the exercises establish selected behaviors.
Adapt one behavior at a time
Choose a concrete next change, such as a different extension or a total row, and write down the expected output first. Preserve the same tests for empty input, spaces and errors. Adding recursion requires a new traversal design; the current glob deliberately visits one directory only.
For portable command patterns, continue with the Unix shell reference. For a report that needs grouping and numeric validation, the shell and SQLite exercise moves those responsibilities into SQL.