Creating a function

Functions are created using the function directive and look similar to declaring functions in other languages. They can be written with or without the parentheses after the function name and don't really need the function directive, but it makes your code look more readable.

function fn() {
	echo "I am a valid function"
}

Calling a function

Functions are called by just calling their name without anything else. You may have seen that in these examples we have not allowed the function to take any arguments. We can of course pass arguments to functions but since this is a bit more complex this has been abstracted to another section 6. Arguments which we will see after this chapter.

function fn() {
	echo "Hi from fn"
}

fn # "Hi from fn"

Return values

It might help to revisit this chapter after you've read 4. Exit codes and return types

When not manually specified, functions are inferred to have succeeded or have a zero exit code (more on this in 4. Exit codes and return types). You could manually exit a function with a zero exit code but this is not required.

It is however helpful to use non-zero exit codes to notify chained commands or other lifecycle events that a command failed or did not do what you expected. There are a bunch of different non-zero exit codes, but you can use 1 as the catch-all for any failure.


An important difference in exiting in functions is that functions can use both exit and return. They look alike but do something different. exit is used to completely end a script or a command, where as return is used to end the function scope:

function returning_fn() {
  echo "I indicate failure but the script continues"
  return 1
}

function exiting_fn() {
  echo "I will stop anything in the script coming after me"
  exit 1
}

returning_fn
exiting_fn
# This echo will never be reached
echo "We will never see this"

Line continuation and function abstraction

Abstracting blocks into functions makes your code, pipeline, automation more readable. I like to abstract every step of my pipeline or task into a separate block, which enhances readability/glancing at code, but also allows a chained pipeline for function calls.

We will explore that pattern of chaining in Operations