Topic 6.3
Functions & Script Arguments
In one line
Functions let you name and reuse a block of logic within a script, and positional arguments ($1, $2, ...) let a script accept input from whoever runs it — together they turn a rigid, hardcoded script into a genuinely reusable tool.
Key ideas
- 01
A bash function is defined as
function_name() { commands; }— once defined (it must appear BEFORE it's called in the script, since bash reads top to bottom), calling it is as simple as writing its name, exactly like any other command:function_name arg1 arg2. - 02
Inside a function,
$1,$2, etc. refer to the ARGUMENTS passed to THAT function specifically (not the script's own overall arguments, if it has any) — this scoping is genuinely important to keep straight, since$1means something different depending on whether you're inside a function or at the script's top level. - 03
A script's own arguments (what you pass on the command line when running it:
./script.sh arg1 arg2) are accessed the same way at the TOP LEVEL:$1is the first argument,$2the second,$0is the script's own name, and$#is the total NUMBER of arguments passed — genuinely useful for validating that the right number of arguments were actually provided before proceeding. - 04
$@represents ALL arguments as separate, individual words (correctly handling ones containing spaces, if properly quoted as"$@"), while$*represents them all as ONE single combined string —"$@"is almost always the correct choice when passing arguments through to another command, since it preserves argument boundaries correctly. - 05
A function can RETURN a value two different ways:
return <number>sets an EXIT CODE (0-255 only, conventionally used for success/failure, not a real value), whileecho <value>followed by capturing the function's output with$(function_name)is the standard way to get an actual VALUE back from a bash function — a genuinely important distinction from most other languages, wherereturntypically hands back arbitrary data directly. - 06
Validating arguments defensively at the top of a script (
if [ $# -lt 1 ]; then echo "Usage: $0 <arg>"; exit 1; fi) is a standard, genuinely important habit — a script that just charges ahead assuming the right arguments were provided fails confusingly deep inside its logic instead of with a clear, immediate error message at the very start.
In your stack
- →
A deployment script might define
deploy() { java -jar "$1" & }and call it asdeploy target/app.jar, or validate arguments upfront withif [ $# -lt 1 ]; then echo "Usage: $0 <jar-path>"; exit 1; fibefore ever attempting to run anything — turning a script that assumes correct usage into one that fails clearly and immediately if misused.
Code & diagrams
A realistic script combining functions, argument validation, and $@ vs $*.
#!/usr/bin/env bash
# Validate the script was called correctly BEFORE doing anything else
if [ $# -lt 1 ]; then
echo "Usage: $0 <environment>"
exit 1
fi
ENVIRONMENT=$1
# A function that returns an actual value via echo + command substitution
get_port_for_env() {
local env=$1
if [ "$env" = "production" ]; then
echo 8080
else
echo 3000
fi
}
# A function that returns only a success/failure exit code
check_file_exists() {
if [ -f "$1" ]; then
return 0
else
return 1
fi
}
PORT=$(get_port_for_env "$ENVIRONMENT")
echo "Deploying to $ENVIRONMENT on port $PORT"
if check_file_exists "app.jar"; then
echo "app.jar found — proceeding"
else
echo "app.jar not found — aborting"
exit 1
fiExplain it without notes
Inside a bash function, what does $1 refer to, and how is that different from $1 at the script's top level?
Why is echo + command substitution the standard way to get a real value out of a bash function, rather than using return?
Practice
Write a script that requires exactly one argument (a filename) and exits with a clear usage message if it's missing, otherwise printing that file's line count.
Write a function that takes two numbers as arguments and echoes their sum, then call it and capture the result into a variable using command substitution.
Trade-offs
- ↔
Bash functions are lightweight and need no imports or setup, but the echo-to-return-a-value pattern is genuinely unintuitive coming from other languages, and functions can't return arbitrary data structures at all (only text) — a strong signal that once a script's logic grows complex enough to need real data structures passed around, it may be time to reach for a genuine scripting language (Python) instead of pushing bash further than it comfortably goes.
Done when you can
I can define and call a bash function, and pass it arguments.
I understand $1 inside a function is different from $1 at the script's top level.
I know to validate a script's arguments ($#) before using them, and I can get a real value out of a function via echo + command substitution.