Introduction
A listening pack should contain every requested preview. If one conversion fails, a partial folder must not look like a completed pack. You will improve a Bash script to prepare files privately, stop honestly on errors, and publish only after all conversions succeed.
This lab starts with its own recordings, a damaged practice copy and an unrelated older pack. It does not use files from the previous lab. Complete Turn One Preview into a Batch first, or know its quoting, FFmpeg options, functions, arrays and native exit statuses. You will also use Bash's trap to clean only the temporary directory your script owns.
Keep Incomplete Conversions Private
In this step, you will make a failed conversion leave no published batch, while keeping earlier work unchanged.
Start in Workspace, which is prepared for UTF-8 filenames. Return here with cd if a relative path is missing.
cd /home/labex/project/audio-studio
ls input library archive/previous
input contains practice copies of three recordings. library contains the allowed intact originals. archive/previous is an older listening pack to preserve, and notes.txt and scratch-other belong to other work. Only change your scripts, practice copies and new experiment/output directories. Supported inputs are non-hidden immediate lowercase .wav regular files: mono, signed 16-bit PCM at 22050 Hz, at least five seconds long.
Read the starter script with cat. Its exit 0 reports success even after a failed conversion, and its early mkdir makes an incomplete pack visible.
cat batch.sh
The following trial intentionally encounters a damaged recording. Expect an FFmpeg error ending in Invalid data found when processing input. That is a file-format failure, not a missing command or a network problem.
bash batch.sh input trial-pack 3
printf 'Starter exit: %s\n' "$?"
It misleadingly says Starter exit: 0. ls trial-pack shows only the successful conversions; the folder alone proves nothing about completeness.
ls trial-pack
Remove only this named disposable trial. rm -r recursively removes a directory and its contents; -- ends option parsing. This command does not touch the old pack, originals or unrelated work.
rm -r -- trial-pack
Our improved contract uses a new output directory on every run. Existing files, directories and symbolic links—including links whose targets are missing—are refused. The output's parent must already exist, and the output must be outside input. There is no supported concurrent writer, and this is protection against conversion failure, not a crash or power-loss transaction guarantee.
A staging directory holds conversions before publication. mktemp -d makes a uniquely named private directory; the XXXXXX suffix is replaced. It lives beside the final output, so the final move stays on the same filesystem. trap '...' EXIT runs cleanup whenever this script exits. Single quotes defer expansion of $stage until cleanup. rm -rf -- "$stage" deletes exactly that owned directory; never replace it with a wildcard such as /tmp/* or .preview.*.
The remaining path operations make that contract explicit. $(command) captures text; || exit 2 stops on failure. realpath -m normalizes a target that does not yet exist, while dirname gets its parent. The case "$output/" prefix test rejects an output within the input directory. Quoting the input prefix makes its literal characters safe while the final * matches a suffix. mv -T moves staging to the exact new directory name rather than nesting it inside another directory. The earlier existence check is not a concurrency lock.
Replace batch.sh with the complete improved version. The array, filename extraction, quoting and FFmpeg arguments work as in the first guided lab.
cat > batch.sh <<'BASH'
#!/usr/bin/env bash
if [ "$#" -ne 3 ]; then printf 'Usage: batch.sh INPUT OUTPUT SECONDS\n' >&2; exit 2; fi
case "$3" in 1|2|3|4|5) seconds=$3 ;; *) printf 'Use 1 to 5 seconds\n' >&2; exit 2 ;; esac
if [ ! -d "$1" ]; then printf 'Input directory missing\n' >&2; exit 2; fi
if [ -e "$2" ] || [ -L "$2" ]; then printf 'Output already exists\n' >&2; exit 2; fi
input=$(realpath -- "$1") || exit 2
output=$(realpath -m -- "$2") || exit 2
parent=$(dirname -- "$output")
if [ ! -d "$parent" ]; then printf 'Output parent missing\n' >&2; exit 2; fi
case "$output/" in "$input/"*) printf 'Output must be outside input\n' >&2; exit 2 ;; esac
shopt -s nullglob
files=("$input"/*.wav)
if [ "${#files[@]}" -eq 0 ]; then printf 'No WAV inputs\n' >&2; exit 1; fi
stage=$(mktemp -d "$parent/.preview.XXXXXX") || exit 1
trap 'rm -rf -- "$stage"' EXIT
for source in "${files[@]}"; do
name=${source##*/}
if ! ffmpeg -nostdin -v error -i "$source" -t "$seconds" -c:a pcm_s16le "$stage/$name"; then
printf 'Could not convert: %s\n' "$name" >&2
exit 1
fi
done
if ! mv -T -- "$stage" "$output"; then exit 1; fi
printf 'Published %s previews\n' "${#files[@]}"
BASH
Try the same damaged practice input. This failure is intentional; do not repair it yet.
bash batch.sh input previews 3
printf 'Protected batch exit: %s\n' "$?"
Expect Could not convert: zz Evening.wav and Protected batch exit: 1. The explicit branch stops the batch. previews must not exist, and the temporary staging directory should already be gone.
ls -a includes hidden names, so you can look for leaked .preview. staging directories. It should still show scratch-other, whose content must stay untouched.
ls -a
Run the check for this step. It exercises your unchanged script in disposable directories; it does not repair your practice input or generate your live pack.
Restore the Practice Copy and Publish
In this step, you will diagnose the failed recording, restore only its permitted practice copy, and use your script to publish a complete pack.
Start in Workspace. FFmpeg can inspect a recording without producing another media file: -f null - selects a null output that discards decoded samples. A successful decode confirms readable audio, while a damaged file returns nonzero. We use this only on the named course practice copy.
ffmpeg -nostdin -v error -i "input/zz Evening.wav" -f null -
printf 'Decode exit: %s\n' "$?"
Expect a format error and a nonzero status. The intact file with the same name is available in library. cp -- copies that original over the damaged practice copy; both quoted paths identify the exact file. Do not modify the library itself.
cp -- "library/zz Evening.wav" "input/zz Evening.wav"
ffmpeg -nostdin -v error -i "input/zz Evening.wav" -f null -
printf 'Decode exit: %s\n' "$?"
Now expect zero. If you mistype the filename, inspect ls library and correct it; there is no need to reset the environment. Run the complete batch with the now-restored input.
bash batch.sh input previews 3
Expect Published 3 previews. Switch to Listening Room and reload. Play each clip under Your listening pack, seek into each, and download one. Each clip should last three seconds and begin with the corresponding recording. This page only reads previews; deleting or damaging a preview cannot make it silently play a library original. Return to Workspace.
Finally, try the existing output once more. This intentional refusal protects the complete pack.
bash batch.sh input previews 3
printf 'Existing output exit: %s\n' "$?"
Expect Output already exists and a nonzero status. Keep that output. To make another pack later, use a new name and a duration from one through five seconds, with its parent directory already present. Your original recordings, older pack, notes and unrelated directory should all remain unchanged.
Summary
You separated preparation from publication, returned failure honestly, and used an EXIT trap to clean one owned staging directory. You diagnosed and restored a damaged practice recording, then published a complete playable pack while preserving earlier work. The challenge applies the same tools to a different listening collection.



