Bash Functions
A function is just a small reusable pease of code. It does something and return its result back to the program. It can also return nothing.
1. Bash Basic function
# Option 1
function function_name() {
# body of function here
}
# Option 2
function_name(){
# body of function here
}
# to call function:
function_name
- Keep in Mind
- Bash functions do not return values. They can return:
- An exit status using
return
- A value using a variable (global or local)
- value via STDOUT - This requests command substitution
- An exit status using
- Bash functions do not return values. They can return:
2. Return an exit status
# Checks if $1 exist. 0 = true, 1 = false
potato(){
[ -f "$1" ] && return 0 || return 1
}
potato "$1"
# We can then use the $? to create logic with the exit status code.
[ $? -eq 0 ] && echo "$1 exist" || echo "$1 doesn't exist"
3. Returning values with variables
# Using a global variable:
potato(){
greeting="Good Morning, $1"
}
potato "Mary"
echo "$greeting"
# Using local variable
potato(){
local name="$1"
local age="$2"
local email="$3"
echo -e "Hello $name\nAt $age you will need a bigger boat.\n\tSent from $email"
}
potato "Mary" "43" "admin@mail.com"
4. Returning value with command substitution
potato() {
local greeting="Hello, $1!"
echo "$greeting"
}
result=$(potato "Bobby")
echo "$result"
5. Can I return a status code and value at the same time?
Yes. Here is an example:
is_even(){
local numb=$1
if (( numb % 2 == 0 ))
then
echo "even"
return 0
else
echo "not even"
return 1
fi
}
result=$(is_even $1)
status_code=$?
echo "the $1 number is $result"
Side Note: The example script above has a couple of gotchas. If you pass a letter instead of a number it tells you that it is even. How so?? Well, bash does arithmetic evaluation using
((...))
inside these parentheses everything is an integer. This means that every non-integer character is considered a 0 by bash. Notice that I said integer and not number. That is because bash does not support floating point numbers. You need an external utility (likebc
) to handle floating point numbers. The next gotcha is the lack of$
in thenumb
variable. As you already know, in order to access the value of a variable, you need to preface it with a$
, however, because the((...))
are already expecting an integer, the$
becomes optional. The script below is a complete version that handles non integer input.
#!/bin/bash
is_even() {
local numb=$1
# Ensure the input is a number
if ! [[ "$numb" =~ ^-?[0-9]+$ ]]; then
echo "Error: Input is not an integer"
return 2 # Use a special return code for invalid input
fi
# Proceed with even/odd check
if (( numb % 2 == 0 )); then
echo "even"
return 0
else
echo "not even"
return 1
fi
}
# Call the function with the user's input
result=$(is_even "$1")
status_code=$?
# Check the return code
if [ $status_code -eq 2 ]; then
echo "Invalid input: $1 is not an integer."
else
echo "The $1 number is $result."
fi
5.1. Whats up with the arguments?
Just like your script, functions take arguments (positional parameters). These work the same way. Say that you run the script ./playground.sh john carla
with the following code:
#!/bin/bash
echo "parameter/argument 1: $1"
echo "parameter/argument 2: $2"
potato(){
echo "In the function 1: $1"
echo "In the function 2: $2"
}
potato "peter" "jane"
echo "Passing the same parameters to the function:"
potato "$1" "$2"
The output will be:
parameter/argument 1: john
parameter/argument 2: carla
In the function 1: peter
In the function 2: jane
Passing the same parameters to the function:
In the function 1: john
In the function 2: carla
Notice that lines 1 and 2 have the arguments we passed to script but lines 3 and 4 have the arguments we passed to the function. You can passed the same arguments you gave to the script to the function too as you can see in lines 7 and 8
6. Move advanced examples of functions
6.1. Returning the content of an array
#!/bin/bash
all_ip_addresses() {
local ips=("192.168.1.10" "192.168.1.12" "192.168.1.14")
echo "${ips[@]}"
}
array=($(all_ip_addresses))
for ip in ${array[@]}
do
ping -c 4 $ip
done
6.2. Returning an array with mapfile
#!/bin/bash
potato() {
local dir="$1"
local fname="$2"
find "$dir" -iname "*$fname" -type f -print0
}
# Use process substitution to pass the function's output to mapfile
mapfile -d '' search < <(potato "$1" "$2")
# Access array elements
echo "First member: ${search[0]}"
echo "Last member: ${search[-1]}"
echo "Length: ${#search[@]}"
# Process each file
for file in "${search[@]}"; do
# Display file details in a formatted way
ls -lhgG --time-style=+%D "$file" | grep -v "total" | column -t
done
Side note: The
print0
option of the find command command is used to handle files with special characters in the name (ex. spaces).print0
tells find to separate filenames with a null character (\0
) instead of a newline.
6.3. Using an associative array
#!/bin/bash
# Function to get disk usage as an associative array
get_disk_usage() {
declare -A disk_info
local df_output
df_output=$(df -h / | tail -1)
disk_info[total]=$(echo "$df_output" | awk '{print $2}')
disk_info[used]=$(echo "$df_output" | awk '{print $3}')
disk_info[available]=$(echo "$df_output" | awk '{print $4}')
disk_info[usage_percent]=$(echo "$df_output" | awk '{print $5}')
# Return the associative array
echo "$(declare -p disk_info)"
}
# Call the function and capture the associative array
eval "$(get_disk_usage)"
# Access and display the values from the associative array
echo "Disk Usage Information for '/':"
echo -e "Total Space:\t${disk_info[total]}"
echo -e "Used Space:\t${disk_info[used]}"
echo -e "Avail. Space:\t${disk_info[available]}"
echo -e "Usage %:\t${disk_info[usage_percent]}"
Side Note:
declare -p disk_info
outputs the array definition as a string that can be re-evaluated usingeval
.