Building a Versatile Shell Script Template with Logging and Command Handling

Page content

Building a Versatile Shell Script Template with Logging and Command Handling

Creating reusable shell scripts can save a lot of time, especially when they’re structured to handle different commands, support logging, and track version history. Here i have provided a reference for a template structure for bash scripting. Hopefully I will continue to use this myself.


Step 1: Setting Up the Script Structure

We start by creating a basic structure for the script, including:

  • Version information: to track the version number and author.
  • Usage and description: to provide instructions on how to use the script.
  • Changelog: a section for documenting changes across different versions.

Below is the initial setup for the template script. Copy this into a file called script_template.sh.

#!/bin/bash

# script_template.sh - A template for command-line scripts with options and logging
# Version: 1.0
# Author: [Your Name]
# Description: A template script to demonstrate structured command-line argument parsing, logging, and modular command processing.
# Usage: ./script_template.sh {command1|command2} [--log]
# Example: ./script_template.sh command1 --log

# Changelog:
# Version 1.0 - [YYYY-MM-DD]: Initial release with basic command handling and logging functionality.
# Version 1.1 - [YYYY-MM-DD]: Added validation for required command arguments and improved error handling.
# Version 1.2 - [YYYY-MM-DD]: Enhanced logging function to support console-only and file logging.
# Version 1.3 - [YYYY-MM-DD]: Minor improvements, added documentation, and refactored functions for modularity.

# Version information
SCRIPT_VERSION="1.0"

# Default log file path
LOG_FILE="script_template.log"
LOG_TO_FILE=false
COMMAND=""

In this setup:

  • Usage and example sections provide users with guidance on using the script.
  • Changelog records updates for each version, with a description of changes.

Step 2: Parsing Command-Line Arguments

Our script template can take commands (e.g., command1 or command2) and an optional --log flag. This flag will enable logging to a file, while commands will determine the actions the script performs.

Here’s the code for parsing arguments:

# Parse command-line arguments
for arg in "$@"; do
    case $arg in
        --log)
            LOG_TO_FILE=true
            shift # Remove --log from processing
            ;;
        command1|command2)
            COMMAND=$arg
            shift # Remove command from processing
            ;;
        *)
            echo "Invalid option: $arg"
            echo "Usage: $0 {command1|command2} [--log]"
            exit 1
            ;;
    esac
done

# Validate that a command was provided
if [ -z "$COMMAND" ]; then
    echo "Missing command. Usage: $0 {command1|command2} [--log]"
    exit 1
fi
  • Argument parsing: The script checks each argument. If --log is present, it sets LOG_TO_FILE=true to enable logging. If a valid command (command1 or command2) is found, it sets that command for execution.
  • Validation: If no command is provided, the script displays usage instructions and exits.

Step 3: Setting Up the Logging Function

This logging function outputs messages to the console and, optionally, to a log file. It uses LOG_TO_FILE to determine if log entries should be saved to a file.

# Log function to output to console and optionally to a log file
log() {
    if $LOG_TO_FILE; then
        echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
    else
        echo "$(date +'%Y-%m-%d %H:%M:%S') - $1"
    fi
}

# Log the start of the script and version
log "Starting script_template.sh - Version $SCRIPT_VERSION"
  • Log timestamp: Each log entry includes a timestamp for easier tracking.
  • Conditional logging: If --log is specified, logs are saved to script_template.log; otherwise, they display only in the console.

Step 4: Defining Commands

We’ll define placeholder functions for each command, allowing easy customization. Each function logs its actions for better transparency.

# Command function for 'command1'
execute_command1() {
    log "Executing command1..."
    # Add code for command1 here
    log "Completed command1."
}

# Command function for 'command2'
execute_command2() {
    log "Executing command2..."
    # Add code for command2 here
    log "Completed command2."
}
  • execute_command1 and execute_command2: These functions represent different actions the script can perform. For example, if this were a deployment script, one command could start the server, and the other could stop it.
  • Customizable functions: These placeholders allow you to add any code you need for each command, keeping the script organized and modular.

Step 5: Running the Commands

With all pieces in place, we add a case statement to call the appropriate function based on the command specified.

# Execute the specified command
case $COMMAND in
    command1)
        execute_command1
        ;;
    command2)
        execute_command2
        ;;
esac

# Log the end of the script execution
log "Completed script_template.sh - Version $SCRIPT_VERSION"

Step 6: Testing the Template

To test the script:

  1. Enable Command Logging: Run with the --log option to write logs to a file.

    ./script_template.sh command1 --log
    
  2. Run Without Logging: Run without --log to print output to the console only.

    ./script_template.sh command2
    
  3. Invalid Commands: Try running the script with an unsupported command to test validation.

    ./script_template.sh invalid_command
    

Script: Full Script

#!/bin/bash

# script_template.sh - A template for command-line scripts with options and logging
# Version: 1.0
# Author: [Your Name]
# Description: A template script to demonstrate structured command-line argument parsing, logging, and modular command processing.
# Usage: ./script_template.sh {command1|command2} [--log]
# Example: ./script_template.sh command1 --log

# Changelog:
# Version 1.0 - [YYYY-MM-DD]: Initial release with basic command handling and logging functionality.
# Version 1.1 - [YYYY-MM-DD]: Added validation for required command arguments and improved error handling.
# Version 1.2 - [YYYY-MM-DD]: Enhanced logging function to support console-only and file logging.
# Version 1.3 - [YYYY-MM-DD]: Minor improvements, added documentation, and refactored functions for modularity.

# Version information
SCRIPT_VERSION="1.0"

# Default log file path
LOG_FILE="script_template.log"
LOG_TO_FILE=false
COMMAND=""

# Parse command-line arguments
for arg in "$@"; do
    case $arg in
        --log)
            LOG_TO_FILE=true
            shift # Remove --log from processing
            ;;
        command1|command2)
            COMMAND=$arg
            shift # Remove command from processing
            ;;
        *)
            echo "Invalid option: $arg"
            echo "Usage: $0 {command1|command2} [--log]"
            exit 1
            ;;
    esac
done

# Validate that a command was provided
if [ -z "$COMMAND" ]; then
    echo "Missing command. Usage: $0 {command1|command2} [--log]"
    exit 1
fi

# Log function to output to console and optionally to a log file
log() {
    if $LOG_TO_FILE; then
        echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
    else
        echo "$(date +'%Y-%m-%d %H:%M:%S') - $1"
    fi
}

# Log the start of the script and version
log "Starting script_template.sh - Version $SCRIPT_VERSION"

# Command function for 'command1'
execute_command1() {
    log "Executing command1..."
    # Add code for command1 here
    log "Completed command1."
}

# Command function for 'command2'
execute_command2() {
    log "Executing command2..."
    # Add code for command2 here
    log "Completed command2."
}

# Execute the specified command
case $COMMAND in
    command1)
        execute_command1
        ;;
    command2)
        execute_command2
        ;;
esac

# Log the end of the script execution
log "Completed script_template.sh - Version $SCRIPT_VERSION"

Conclusion

This versatile script template provides a powerful foundation for creating structured, modular shell scripts. By including logging, command validation, and a changelog.