Functions, Local Variables, and Return Status
Functions turn shell statements into reusable internal commands. A good function has explicit inputs, local state, a clear success/failure contract, and predictable output channels.
Learning objectives
By the end of this lesson
- Define and invoke Bash functions.
- Use local variables to reduce shared state.
- Return status separately from data output.
- Validate function arguments.
- Control side effects so functions remain composable.
1. Functions create named command units
deploy_service() {
printf 'deploying\n'
}
deploy_serviceFunctions run in the current shell environment unless you explicitly introduce a subshell.
2. Function arguments use positional parameters
deploy_service() {
local service=$1
local environment=$2
printf 'service=%s env=%s\n' "$service" "$environment"
}
deploy_service api stagingInside the function, $1, $2, $#, and "$@" refer to the function call.
3. local avoids accidental global mutation
name="global"
demo() {
local name="function-local"
printf 'inside=%s\n' "$name"
}
demo
printf 'outside=%s\n' "$name"Declare function-internal variables with local unless modifying shared shell state is intentionally part of the interface.
4. Bash local variables are dynamically scoped
outer() {
local mode="staging"
inner
}
inner() {
printf 'mode=%s\n' "$mode"
}
outerCalled functions can see active locals from callers. Prefer explicit arguments for dependencies that matter to readability and testing.
5. return communicates command status
is_supported_env() {
case $1 in
dev|staging|prod) return 0 ;;
*) return 1 ;;
esac
}
if is_supported_env staging; then
printf 'supported\n'
fireturn does not return an arbitrary string object. It sets the function's exit status.
6. Emit result data on stdout when appropriate
artifact_path() {
local service=$1
printf 'looking up %s\n' "$service" >&2
printf '/srv/artifacts/%s.tar.gz\n' "$service"
}
artifact=$(artifact_path api)
printf 'artifact=%s\n' "$artifact"When callers use command substitution, stdout is a data interface. Diagnostics belong on stderr.
7. Check status before trusting captured output
lookup_version() {
local service=$1
if [[ $service == api ]]; then
printf '2.14.7\n'
return 0
fi
printf 'unknown service\n' >&2
return 1
}
if version=$(lookup_version api); then
printf 'version=%s\n' "$version"
fi8. Validate at the function boundary
require_file() {
local path=${1:-}
if [[ -z $path ]]; then
printf 'require_file: path required\n' >&2
return 64
fi
if [[ ! -r $path ]]; then
printf 'unreadable: %s\n' "$path" >&2
return 1
fi
}Early validation keeps empty or malformed state from leaking into lower-level operations.
9. Preserve failure status before cleanup
run_with_cleanup() {
local tmp status
tmp=$(mktemp) || return 1
if perform_work "$tmp"; then
status=0
else
status=$?
fi
rm -f -- "$tmp"
return "$status"
}Otherwise the cleanup command may accidentally become the function's final status.
10. Confine directory and option side effects
with_workspace() {
local workspace=$1
(
cd "$workspace" || exit 1
run_build
)
}The subshell prevents the directory change from leaking into the caller.
11. Name functions at the domain level
Names such as validate_config, fetch_artifact, wait_for_ready, and deploy_service describe intent. Names such as helper2 merely hide code behind another label.
12. Hands-on lab: function-based deployment plan
mkdir -p "$HOME/devops-academy/bash/chapter05/lesson04"
cd "$HOME/devops-academy/bash/chapter05/lesson04"
cat > functions.sh <<'EOF'
#!/usr/bin/env bash
validate_environment() {
local environment=${1:-}
[[ $environment == dev || $environment == staging || $environment == prod ]]
}
artifact_path() {
local service=${1:-}
[[ -n $service ]] || return 64
printf './artifacts/%s.tar.gz\n' "$service"
}
report_plan() {
local service=$1
local environment=$2
local artifact=$3
printf 'PLAN service=%s env=%s artifact=%s\n' "$service" "$environment" "$artifact"
}
service=${1:-api}
environment=${2:-staging}
validate_environment "$environment" || exit 2
artifact=$(artifact_path "$service") || exit $?
report_plan "$service" "$environment" "$artifact"
EOF
bash functions.sh api staging
bash functions.sh api qa || trueVerification checklist
13. Knowledge check
Question 1. What does return 0 mean?
Question 2. Why use local?
Question 3. How should a function provide a string result to command substitution?
Question 4. Why save a failure status before cleanup?
14. Summary
Bash functions are internal command interfaces. Give them explicit arguments, local state, clean stdout/stderr behavior, and documented return statuses so they can be reused and tested safely.
15. Further reading
- GNU Bash Reference Manual — Shell Functions.
- GNU Bash Reference Manual —
localandreturn. - GNU Bash Reference Manual — Shell Parameters.
- ShellCheck documentation — function scope and status.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.