Bash Functions

By 

•

Updated on

•

11 min read

Bash Functions

As your Bash scripts grow, you will often find yourself repeating the same group of commands in several places. Copying that code around makes the script longer and harder to change.

A Bash function is a reusable block of commands that can be called multiple times within a script. Functions help you make your Bash scripts more readable, easier to maintain, and less repetitive.

While Bash functions are more limited than those in other programming languages, they are still very powerful for shell scripting.

In this guide, we will cover the basics of Bash functions, including how to pass arguments and return values, and show you how to use them in your shell scripts. If you are new to Bash, consider reviewing the basic Linux commands first.

Defining Bash Functions

There are two different ways to define a function in Bash.

  1. Preferred Syntax (Most Common) The first format starts with the function name, followed by parentheses. This is the preferred and most-used syntax.

    sh
    function_name () {
      commands
    }

    Single line version:

    sh
    function_name () { commands; }
  2. Using the function Keyword The second format starts with the reserved word function, followed by the function name.

    sh
    function function_name {
      commands
    }

    Single line version:

    sh
    function function_name { commands; }

You may also see the two styles combined, with the function keyword followed by parentheses:

sh
function function_name () {
  commands
}

Bash accepts all of these forms. POSIX shells such as dash accept only the first one. The other two forms fail with a syntax error.

Important points to note:

  • The commands inside the curly braces ({}) make up the function body.
  • The opening and closing braces must be separated from the body by spaces or newlines.
  • A function runs only when explicitly called by name.
  • The function must be defined before it is called.
  • When using single-line “compacted” functions, a semicolon ; is required after the last command.
  • Use descriptive function names whenever possible.
Tip
Stick to the first syntax (function_name () {}) for better portability and readability.

Example: Hello World Function

To understand this better, take a look at the following example:

~/hello_world.shsh
#!/bin/bash

hello_world () {
   echo 'Hello, World.'
}

hello_world

Here is what each line does:

  • In line 3, we are defining the function by giving it a name. The curly brace { marks the start of the function’s body.
  • Line 4 is the function body. The function body can contain multiple commands, statements, and variable declarations.
  • Line 5, the closing curly brace }, defines the end of the hello_world function.
  • In line 7, we are executing the function. You can execute the function as many times as you need.

Running the script prints:

output
Hello, World.

Variable Scope in Bash Functions

Knowing how variable scope works helps you write scripts that are safe and predictable.

Global Variables

  • Global variables are variables that can be accessed and modified anywhere in the script, including inside functions.
  • Assignments update the nearest visible variable. A new variable is global unless explicitly declared local.
  • The declare and typeset builtins are the exception. When used inside a function, they create a local variable unless you add the -g option.

Local Variables

  • Local variables are declared with the local keyword.
  • The local keyword can be used only inside a function.
  • A local variable is visible inside the function that declares it and in any function called from that function. Bash uses dynamic scoping, not the lexical scoping found in most programming languages.
  • You can have local variables with the same name in different functions.
  • Local variables override global variables with the same name within the function.
Tip
Always use local variables inside functions unless you explicitly need global access.

Example: Variable Scope

To better illustrate how variable scope works in Bash, consider the following example:

~/variables_scope.shsh
#!/bin/bash

var1='A'
var2='B'

my_function () {
  local var1='C'
  var2='D'
  echo "Inside function: var1: $var1, var2: $var2"
}

echo "Before executing function: var1: $var1, var2: $var2"

my_function

echo "After executing function: var1: $var1, var2: $var2"

The script starts by defining two global variables var1 and var2. Then there is a function that sets a local variable var1 and modifies the global variable var2.

If you run the script, you should see the following output:

output
Before executing function: var1: A, var2: B
Inside function: var1: C, var2: D
After executing function: var1: A, var2: D

From the output above, we can conclude that:

  • When a local variable is defined within a function with the same name as an existing global variable, the local variable takes precedence.
  • Global variables can be changed from within the function.

Local variables are not limited to the function that declares them. A function called from that function can also read them:

~/dynamic_scope.shsh
#!/bin/bash

show_name () {
  echo "show_name sees: $name"
}

set_name () {
  local name="Zoe"
  show_name
}

set_name
echo "after set_name: '$name'"
output
show_name sees: Zoe
after set_name: ''

The name variable is local to set_name, but show_name still prints Zoe. When set_name returns, the local variable is removed, and name is empty again in the main script.

Return Values in Bash Functions

Unlike functions in most other programming languages, Bash functions do not return values when called.

When a Bash function completes, its return value is the status of the last statement executed in the function:

  • 0 for success.
  • Any non-zero value in the (1 - 255) range for failure.

The return status can be specified using the return keyword, and it is assigned to the variable $?. The return statement terminates the function. You can think of it as the function’s exit status .

The status is always a number from 0 to 255. Bash keeps only the lowest 8 bits of the value you pass to return, so return 256 gives 0, and return 300 gives 44. If you use return without a number, the function returns the status of the last command it ran.

In the following example, the function prints a message and returns the status 55:

~/return_values.shsh
#!/bin/bash

my_function () {
  echo "some result"
  return 55
}

my_function
echo $?
output
some result
55

The first line comes from the echo command inside the function. The second line is the value of $?. It holds the status set by return 55.

Using the Return Status in Conditions

The return status is most useful in conditional statements. The if statement checks the exit status of a command, and a function call works the same way:

~/check_number.shsh
#!/bin/bash

is_number () {
  [[ $1 =~ ^[0-9]+$ ]]
}

if is_number "42"; then
  echo "42 is a number"
fi

if ! is_number "abc"; then
  echo "abc is not a number"
fi
output
42 is a number
abc is not a number

The is_number function has no return statement. Its status comes from the [[ ]] test, the last command in the function. The test returns 0 when the argument contains only digits and 1 otherwise.

Returning Data from a Bash Function

Since Bash cannot return arbitrary values directly, we need to use other methods. There are two common workarounds.

  1. Using a Global Variable:

    The simplest option is to assign the result of the function to a global variable:

    ~/global_result.shsh
    #!/bin/bash
    
    my_function () {
      func_result="some result"
    }
    
    my_function
    echo "$func_result"
    output
    some result
  2. Using Standard Output (Recommended)

    Another option is to send the value to stdout using echo or printf . This approach is cleaner and avoids unnecessary global variables:

    ~/stdout_result.shsh
    #!/bin/bash
    
    my_function () {
      local func_result="some result"
      echo "$func_result"
    }
    
    func_result="$(my_function)"
    echo "$func_result"
    output
    some result

    Instead of simply executing the function, which prints the message to stdout, we assign the function’s output to the func_result variable using the $() command substitution. The variable can later be used as needed.

Command substitution runs the function in a subshell . Any changes the function makes to global variables are lost when the subshell exits:

~/subshell_counter.shsh
#!/bin/bash

counter=0

increment () {
  counter=$((counter + 1))
  echo "$counter"
}

result="$(increment)"
echo "result: $result, counter: $counter"
output
result: 1, counter: 0

The function increments counter and prints 1, but the change happens inside the subshell. The global counter in the main script is still 0. If a function needs to update global variables, call it directly, without $().

Passing Arguments to Functions

Arguments are passed to a function by placing them after the function name, separated by a space. It is a good practice to double-quote the arguments to avoid the misparsing of an argument with spaces in it.

sh
function_name arg1 arg2 arg3

Special Variables

  • Positional parameters $1, $2, $3 … $n, correspond to the position of the parameter after the function’s name.
  • The $0 variable holds the name of the script. To get the current function’s name from inside the function, use ${FUNCNAME[0]}.
  • The $# variable holds the number of positional parameters/arguments passed to the function.
  • The $* and $@ variables hold all positional parameters/arguments passed to the function.
    • When double-quoted, "$*" expands to a single string separated by space (the first character of IFS) - "$1 $2 $n".
    • When double-quoted, "$@" expands to separate strings - "$1" "$2" "$n".
    • When not double-quoted, $* and $@ are the same.

Here is an example:

~/passing_arguments.shsh
#!/bin/bash

greeting () {
  echo "Hello $1"
}

greeting "Zoe"
output
Hello Zoe

The string Zoe is the first argument, so $1 expands to Zoe inside the function.

The next example uses $# and "$@" to work with all arguments passed to the function:

~/print_args.shsh
#!/bin/bash

print_args () {
  local arg
  echo "Number of arguments: $#"
  for arg in "$@"; do
    echo "Argument: $arg"
  done
}

print_args "Zoe" "Luca Rossi" "Leah"
output
Number of arguments: 3
Argument: Zoe
Argument: Luca Rossi
Argument: Leah

The $# variable reports three arguments. "Luca Rossi" is quoted in the function call, so "$@" keeps it as a single argument. Without the quotes, Bash would split it into two words, and the function would receive four arguments.

A function can also check its arguments and set defaults for optional ones. The following function returns an error status when no name is given and uses Hello when the second argument is missing:

~/greeting.shsh
#!/bin/bash

greeting () {
  if [[ $# -eq 0 ]]; then
    echo "Usage: greeting NAME [MESSAGE]" >&2
    return 1
  fi

  local name="$1"
  local message="${2:-Hello}"

  echo "$message, $name!"
}

greeting "Zoe"
greeting "Leah" "Good morning"
greeting
output
Hello, Zoe!
Good morning, Leah!
Usage: greeting NAME [MESSAGE]

The ${2:-Hello} parameter expansion returns Hello when the second argument is unset or empty. The third call passes no arguments, so the function prints the usage message to standard error and returns 1.

Inside a function, $1, $2, and the other positional parameters refer to the function arguments, not to the arguments of the script. To pass all script arguments to a function, call it with "$@":

sh
main "$@"

For more details on how these variables work, see our guide on Bash positional arguments .

Bash Function Best Practices

  • Choose function names that clearly describe what the function does.
  • Declare variables as local inside functions to avoid unexpected behavior.
  • Return the data via standard output rather than relying on global variables.
  • Be sure to quote variables, such as "$var".
  • Write functions that are short and handle only one specific task.
  • Put your function definitions at the top of your scripts.
  • Turn on strict mode with set -euo pipefail when writing scripts for production.
  • Add comments to explain any complex logic inside your functions.

Troubleshooting

Function call fails with “command not found”
Bash reads a script from top to bottom, and a function exists only after its definition has run. Calling it earlier prints an error such as ./script.sh: line 3: say_hello: command not found. Move the function definition above the first call.

Syntax error in a one-line function
A one-line function needs a semicolon after the last command, as in say_hello () { echo "Hello"; }. Without it, Bash treats the closing } as an argument to echo and reports syntax error: unexpected end of file from `{' command on line 3.

local: can only be used in a function
The local keyword works only inside a function body. Remove local from variables in the main part of the script, or move that code into a function.

Variable changes are lost after calling a function
A function called with command substitution, such as result="$(my_function)", runs in a subshell. Changes it makes to global variables disappear when the subshell exits. Call the function directly when it needs to update global variables.

Conclusion

Bash functions let you group commands into reusable blocks, keep variables local, and pass data in and out through arguments, return statuses, and standard output. To reuse functions across scripts, keep them in a separate file and load it with the source command . For short command shortcuts, see our guide on Bash aliases .

Tags

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page