Advanced Shell Scripting Functions

Defining and Invoking Shell Functions

Shell functions allow you to encapsulate repetitive logic into reusable blocks, improving script organization and maintainability. Functions can be defined directly in a script or interactively within a terminal session.

monitor_process() {
    echo "Monitoring logic goes here";
}

Building a Process Watchdog

A common operational use case for shell functions is creating a daemon to monitor critical services. The following script continuously checks if a specific service is active, restarting it automatically if it crashes.

#!/bin/bash
# watchdog.sh
current_pid=$$

while true; do
    if pgrep -x nginx > /dev/null; then
        echo "Service nginx is active."
        sleep 5
    else
        systemctl restart nginx
        echo "Service nginx was inactive. Attempting restart..."
    fi
done

To execute this script in the background as a persistent daemon, allowing it to survive terminal disconnections:

nohup bash watchdog.sh &

View the generated output logs in real-time:

tail -f nohup.out

Passing Arguments to Functions

Arguments are passed to shell functions in the same manner as passing parameters to a script itself, utilizing positional variables such as $1, $2, and $3.

The following calculator script accepts two numeric operands and an arithmetic operator as arguments:

#!/bin/bash
# compute.sh

calculate() {
    local val_a=$1
    local operator=$2
    local val_b=$3

    case $operator in
        +) echo "$((val_a + val_b))" ;;
        -) echo "$((val_a - val_b))" ;;
        x) echo "$((val_a * val_b))" ;;
        /) echo "$((val_a / val_b))" ;;
    esac
}

calculate $1 $2 $3

Execute the script by providing the required parameters:

bash compute.sh 45 x 12

Handling Function Output and Status

Functions can communicate back to the calling environment using two primary mechanisms: exit statuses via return, and data streams via echo.

Using return for Status: The return directive outputs an integer exit code (0 for success, non-zero for failure), which is ideal for boolean logic and conditional checks.

#!/bin/bash
# service_status.sh

is_active() {
    if pgrep -x nginx > /dev/null; then
        return 0
    else
        return 1
    fi
}

is_active && echo "Nginx is operational." || echo "Nginx is offline."

Using echo for Data: When a function must output strings, arrays, or computed values, echo is utilized. The calling script can capture this output using command substitution.

#!/bin/bash
# list_accounts.sh

fetch_accounts() {
    awk -F: '{print $1}' /etc/passwd
}

registered_users=$(fetch_accounts)
counter=1

for account in $registered_users; do
    echo "Account $counter: $account"
    ((counter++))
done

Understanding Variable Scope

Variables declared inside a function without the local keyword become globally accessible once the function is executed. Conversely, variables defined with local are strictly confined to the function's execution context and are destroyed upon function completion.

#!/bin/bash
# scope_demo.sh

global_msg="System initialized"

assign_vars() {
    inner_global="Accessible everywhere"
    local confined="Restricted context"
}

assign_vars

echo "$global_msg"
echo "$inner_global"
echo "$confined" # This will output an empty line

Creating Reusable Function Libraries

To promote code reuse across multiple scripts, common functions can be extracted into standalone library files. These libraries are then imported into a script using the source command (or its shorthand .).

Library file located at /usr/local/lib/sys_math.lib:

#!/bin/echo "Warning: This is a library, do not execute directly."

sum() {
    echo "$(($1 + $2))"
}

subtract() {
    echo "$(($1 - $2))"
}

multiply() {
    echo "$(($1 * $2))"
}

divide() {
    echo "$(($1 / $2))"
}

show_resources() {
    echo "=== Memory Utilization ==="
    free -h
    echo "=== Disk Usage ==="
    df -h
}

Main application script importing the library:

#!/bin/bash
# app.sh

source /usr/local/lib/sys_math.lib

sum 15 85
subtract 100 35
multiply 6 9
divide 90 3
show_resources

Best practices for managing shell function libraries:

  • Adopt a conventional extension like .lib for easy identification.
  • Library files do not require executable permissions, as they are sourced rather than executed.
  • Store libraries in dedicated directories (e.g., /usr/local/lib) and reference them using absolute paths.
  • Use #!/bin/echo as the shebang to prevent accidental direct execution, which will simply print the warning message.

Tags: bash shell-scripting function-library variable-scope process-monitoring

Posted on Tue, 03 Nov 2026 16:11:23 +0000 by shadypalm88