The cleanup that deleted the wrong directory
A deploy script had a helper, prepare(), that built a release into a temp path and stored it in a variable named target. The main script also used target — it was the directory the cleanup step removed at the end with rm -rf "$target". prepare() did not declare target as local, so calling it overwrote the main script's target with the temp path. On the run where the temp build failed halfway, target was left pointing at the wrong place, and rm -rf "$target" removed a directory that was very much not temporary. We restored from backup. The ten minutes of dread before the backup finished is the part I will not repeat.
The bug was not rm. It was a function leaking a variable into its caller because bash makes variables global unless you say otherwise.
Functions take arguments as $1, $@, $#
Inside greet_all, $1 is its own first argument, $# is the count, and "$@" is every argument kept as separate items — the quotes are what keep db primary from splitting into two. Functions reuse the same positional parameters the script does, scoped to the call.
Declare variables local — the fix for the outage
local target confines the variable to the function; the caller's target is never touched. Note how prepare hands its result back — it echos the path and the caller captures it with $(prepare). That is the correct way to return data.
return is a status, not a value
return only carries an exit status, 0–255, and wraps above that — return 300 hands back 44. So return answers "did it work," and echo plus command substitution answers "what is the value." Mixing them up is how a function that counts 300 files convinces the caller it counted 44.
Parse flags with getopts, not by hand
The colon after f marks -f as needing an argument, which lands in $OPTARG. getopts handles flag order, bundling (-vf file), and missing arguments — all the things hand-rolled $1 parsing gets subtly wrong.
local everything, return data with echo, and parse flags with getopts. A function that fails should fail loudly — wrap the script in set -euo pipefail, and reach for a for loop when a function needs to act on a list.