Bash function: define, call, arguments, `local`, and `return`

Bash define function with name() or function keyword, bash function example calls, bash script function arguments ($1, $@), local vs global variables, return exit codes, and capturing output.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Bash function: define, call, arguments, `local`, and `return`

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:

bash
say() {
  echo "hello"
}

The function keyword (also Bash; slightly more verbose):

bash
function say2 {
  echo "hello again"
}

Call either one by name:

bash
say
say2

Output:

text
hello
hello again

Pick 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.

bash
show() {
  echo "first=$1 second=$2"
  echo "all together: $*"
}

show "one word" "two"

Output:

text
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 of IFS (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.

bash
f_global() { x=2; }
x=1
f_global
echo "x after f_global (no local): $x"

Output:

text
x after f_global (no local): 2

With local, the outer x stays put:

bash
f_local() {
  local x=3
  echo "inside: $x"
}
x=1
f_local
echo "x after f_local: $x"

Output:

text
inside: 3
x after f_local: 1

Rule 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.

bash
check() { return 2; }
check
echo "return exit: $?"

Output:

text
return exit: 2

To 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.

bash
answer() { echo "result"; }
out=$(answer)
echo "captured: $out"

Output:

text
captured: result

That 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 return for status; use echo/printf + $() for data.
  • Avoid return outside a function—it is not exit.

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:

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …