2.7 Terminal Integration Exercise
Prerequisites: Pages 1 through 5 of this chapter
Estimated time: 20–25 minutes
Exercise environment: Bash 5 on GNU/Linux
You are ready to continue when: you can complete the exercise independently and explain each expansion and redirection
Complete one continuous task from an empty directory. The experiment does not modify startup files in the home directory or depend on /var/log; every output stays inside the directory created by mktemp.
Task 1: Establish Project Skeleton
workdir=$(mktemp -d)
printf 'workdir=%s\n' "$workdir"
cd "$workdir"
mkdir -p project/{src,docs,logs,bin}
printf '%s\n' '# Workshop Project' > project/README.md
printf '%s\n' 'first note' > project/docs/notes.txt
printf '%s\n' 'second note' >> project/docs/notes.txt
touch project/logs/app-{01,02,03}.log
find project -printCheckpoints: Directory structure is complete; notes.txt has two lines; three log files exist.
Task 2: Create a Command Effective Only in the Practice Area
cat > project/bin/workshop-status <<'SCRIPT'
#!/bin/sh
printf 'root=%s\n' "$WORKSHOP_ROOT"
find "$WORKSHOP_ROOT" -type f -print
SCRIPT
chmod +x project/bin/workshop-status
export WORKSHOP_ROOT="$workdir/project"
original_path=$PATH
PATH="$workdir/project/bin:$PATH"
export PATH
type -a workshop-status
workshop-statusCheckpoint: type -a points to a temporary directory; child processes can read WORKSHOP_ROOT.
Task 3: Preview Batch Renaming
cd "$WORKSHOP_ROOT/logs"
for file in ./*.log; do
[ -e "$file" ] || continue
printf 'would rename: %q -> %q\n' "$file" "${file%.log}.bak"
doneConfirm all three targets are correct before executing:
for file in ./*.log; do
[ -e "$file" ] || continue
mv -- "$file" "${file%.log}.bak"
done
printf '%s\n' ./*.bakCheckpoints: Only the file extensions were changed, and the file content remained unchanged. If processing images, renaming does not equate to format conversion.
Task 4: Observe Standard Output and Standard Error
cd "$workdir"
sh -c 'printf "normal\n"; printf "problem\n" >&2; exit 7' \
>out.log 2>err.log || status=$?
printf 'status=%s\n' "${status:-0}"
printf 'stdout: '; cat out.log
printf 'stderr: '; cat err.logCheckpoints: The exit status is 7, and each file has one line. Then, change the redirection to >all.log 2>&1 and observe how the two lines end up in the same file.
Task 5: Reliably Traverse and Count
count=0
while IFS= read -r -d '' file; do
((count += 1))
done < <(find project -type f -print0)
printf 'files=%d\n' "$count"After these steps, expect files=6: the README, notes, custom script, and three logs. out.log and err.log are outside project, so they are excluded. The loop does not propagate the exit status of find inside process substitution; separately check read failures on real directories. NUL-delimited iteration handles spaces and newlines in filenames more reliably than parsing ls. The code uses Bash's process substitution and read -d; if the script must be portable across /bin/sh, macOS, and Linux, the implementation should be reselected to one that is supported across all three platforms, rather than by changing the first line interpreter alone.
Troubleshooting Order
pwd: Is it in the expected directory?printf '<%s>\n' "$variable": Is the variable empty or contains unexpected whitespace?type -a command: Which command is actually being executed?ls -ld -- "$path": Does the target exist and does it have sufficient permissions?printf '%q\n' "$path": What is the expanded path in Bash?status=$?: Which step failed? Do not run another command before reading the status.
Cleaning and Acceptance
cd
ls -ld -- "$workdir"
rm -r -- "$workdir"
PATH=$original_path
export PATH
unset original_path WORKSHOP_ROOT
hash -r 2>/dev/null || trueTo check your understanding, repeat all five tasks in a fresh directory without looking at the answers. After completing them, you should be able to explain why variables are quoted, why the -- is used before the path, and why the exercises don't directly modify the Shell startup file.
You can now confirm the current location, inspect command resolution, and review the scope of batch operations before execution. The next chapter uses Git to record file changes so committed states can be compared and restored.