Sagar.BlogArticle
All posts
All posts
Bash

Function Arguments, Return Codes, and Output Capture

Deep dive into how Bash functions receive arguments, signal success or failure with return codes, and communicate results back to callers.

January 17, 20266 min read
BashFunctionsReturn CodesScripting

Understanding the contract between a function and its caller — how arguments flow in, how results flow out, and how success/failure is signalled — is essential for writing Bash functions that compose reliably.

Positional Parameters Inside Functions

function show_args {
    echo "Function name (via FUNCNAME): ${FUNCNAME[0]}"
    echo "Number of args: $#"
    echo "All args: $@"
    echo "First: $1"
    echo "Second: $2"
}

show_args "alpha" "beta" "gamma"
# Function name: show_args
# Number of args: 3
# All args: alpha beta gamma
# First: alpha
# Second: beta

# Note: $0 inside a function is the SCRIPT name, not the function name
# Use ${FUNCNAME[0]} for the function name

Validating Arguments

function create_backup {
    local src="${1:?create_backup: source directory required}"
    local dest="${2:?create_backup: destination directory required}"

    if [[ ! -d "$src" ]]; then
        echo "ERROR: source '$src' is not a directory" >&2
        return 1
    fi

    mkdir -p "$dest" || return 1
    cp -r "$src/." "$dest/" || return 1
    echo "Backup complete: $src$dest"
    return 0
}

# Call and check result
if create_backup "/etc" "/tmp/etc-backup"; then
    echo "Success"
else
    echo "Backup failed" >&2
fi

Return Codes vs Output

Two communication channels

ChannelHowUse for
Exit/return codereturn N (0=success)Success/failure signalling
Standard outputecho "value"Return data values
Standard errorecho "msg" >&2Error messages, logging

Callers check exit codes with if, $?, &&, || and capture output with $().

function get_file_size {
    local file="$1"
    if [[ ! -f "$file" ]]; then
        echo "File not found: $file" >&2    # error → stderr
        return 1                             # signal failure
    fi
    stat -c%s "$file" 2>/dev/null || wc -c < "$file"    # size → stdout
    return 0                                 # signal success
}

# Use it
size=$(get_file_size "/etc/hosts")
if [[ $? -eq 0 ]]; then
    echo "Size: $size bytes"
else
    echo "Could not get size"
fi

# Cleaner idiom:
if size=$(get_file_size "/etc/hosts"); then
    echo "Size: $size bytes"
fi

Passing Arrays to Functions

Bash can't pass arrays directly — they're word-split at the call site. Use the nameref trick (Bash 4.3+) or pass via a global variable:

# Method 1: declare -n nameref (Bash 4.3+)
function print_array {
    local -n arr=$1    # -n makes arr a nameref to the named variable
    for item in "${arr[@]}"; do
        echo "  - $item"
    done
}

fruits=("apple" "banana" "cherry")
print_array fruits    # pass the NAME, not the content

# Method 2: use "$@" when the arguments ARE the array elements
function sum_args {
    local total=0
    for n in "$@"; do (( total += n )); done
    echo "$total"
}

numbers=(1 2 3 4 5)
total=$(sum_args "${numbers[@]}")
echo "Sum: $total"
Quick Check

A function runs `return 42`. What does `$?` hold immediately after the function call?

Exercise

Write a function find_largest that:

  1. Takes any number of numeric arguments (use "$@")
  2. Finds and echoes the largest number
  3. Returns 0 on success, 1 if no arguments given
  4. Test it: largest=$(find_largest 3 1 7 2 5) should give largest=7