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 thenumbvariable. 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
print0option of the find command command is used to handle files with special characters in the name (ex. spaces).print0tells 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_infooutputs the array definition as a string that can be re-evaluated usingeval.