Functions in bash let you name a block of commands and call it like a mini-program.
You might land here from searches like bash function, bash define function, or bash
script function—all of the same pieces: how you declare the body, how arguments map
to $1, $2, "$@", how variable scope works, and how return differs from
printing a value. For a broader Bash path, the
Shell Scripting & Bash course hub links
this topic with loops and the rest of the series.
Tested all the commands and code from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.
Bash define function (two valid shapes)
The portable POSIX-style form:
say() {
echo "hello"
}The function keyword (also Bash; slightly more verbose):
function say2 {
echo "hello again"
}Call either one by name:
say
say2Output:
hello
hello againPick one style per project and stay consistent; both are widely used in real scripts.
Bash function example: parameters ($1, $2, $@)
Inside a function, $1 … $9 (and beyond with ${10}) are the arguments passed at
call time—not the script’s original argv unless you forward them yourself.
show() {
echo "first=$1 second=$2"
echo "all together: $*"
}
show "one word" "two"Output:
first=one word second=two
all together: one word two"$@"— each argument as its own word (best default when re-passing)."$*"— one string joined by the first character ofIFS(handy sometimes, easy to misuse).
To turn arguments into an array inside the function, use args=("$@") and read
split string into array when
you are building lists from text.
local vs global (the important detail)
A plain assignment inside a function updates a global shell variable unless you
use local (or declare a local in a subshell context). That surprises people who
expect “function scope” by default.
f_global() { x=2; }
x=1
f_global
echo "x after f_global (no local): $x"Output:
x after f_global (no local): 2With local, the outer x stays put:
f_local() {
local x=3
echo "inside: $x"
}
x=1
f_local
echo "x after f_local: $x"Output:
inside: 3
x after f_local: 1Rule of thumb: every temporary variable inside a library-style function should be
local unless you deliberately mean to set a global.
Returning status vs returning data
return n sets the function’s exit status for the caller ($? right after the
call). Status codes are limited to 0–255; use them for success/failure, not large
numbers or strings.
check() { return 2; }
check
echo "return exit: $?"Output:
return exit: 2To hand a string or number “back” to the caller, print and capture with command substitution, or refactor to write a result variable the caller names.
answer() { echo "result"; }
out=$(answer)
echo "captured: $out"Output:
captured: resultThat pattern is how most bash function example snippets pass data without abusing
return.
Grouping: { …; } vs ( … )
A brace group runs in the current shell; parentheses run in a subshell, so assignments there do not touch the parent’s variables. Functions already run as a unit; subshells show up more when you wrap pipelines—see while loops for related control-flow patterns.
Small habits that keep functions maintainable
- Put a one-line comment above non-obvious functions: purpose, parameters, side effects.
- Quote
"$1"when passing paths or user text. - Prefer
returnfor status; useecho/printf+$()for data. - Avoid
returnoutside a function—it is notexit.
Summary
Define functions with name() { … } or function name { … }, call them like
commands, and use $1 / "$@" for arguments. Mark temporaries with local so you
do not overwrite globals by accident. Use return only for small integer statuses;
print and capture when the caller needs real data back. That covers most bash
function patterns you will use in production scripts.
Further reading:
- GNU Bash manual — Shell functions
- Bash for loop when you factor loop bodies into functions

