Problem Statement
What are best practices for organizing large shell scripts? Discuss structure, modularity, and maintainability.
Explanation
Structure template:
```bash
#!/bin/bash
# Script header: description, usage, author, version
set -euo pipefail # Strict mode
# Constants (UPPERCASE)
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
readonly CONFIG_FILE="${SCRIPT_DIR}/config.conf"
# Default variables (lowercase)
verbose=0
log_file="/var/log/${SCRIPT_NAME}.log"
# Functions
usage() { ... }
log() { ... }
error() { ... }
validate_input() { ... }
main() { ... }
# Parse arguments
while getopts "vhf:" opt; do ...; done
# Main execution
main "$@"
```
Modularity: break into logical functions (single responsibility), source common functions from libraries, separate configuration from code, use function names describing action. Group related functions: all validation functions together, all logging functions together, main logic functions.
Library organization:
```
project/
├── bin/
│ └── main_script.sh
├── lib/
│ ├── common.sh # Shared utilities
│ ├── logging.sh # Logging functions
│ └── validation.sh # Input validation
├── conf/
│ └── config.conf
└── README.md
```
Source libraries: source "${SCRIPT_DIR}/../lib/common.sh". Check if sourced: [[ -n ${COMMON_LIB:-} ]] && return prevents double-sourcing.
Configuration management: external config files for environment-specific values, validate config on load, provide defaults, document required variables. Example config.conf:
```bash
# Database config
DB_HOST="localhost"
DB_PORT=5432
DB_NAME="mydb"
```
Load: [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE" || error "Config not found".
Documentation: header comments explaining purpose and usage, function comments describing parameters and return values, inline comments for complex logic only, usage function showing examples, README for multi-script projects. Version control: use git, tag releases, maintain changelog.
Error handling: validate all inputs, check command exit status for critical operations, provide meaningful error messages with context, log errors to file and stderr, clean up on exit (trap), fail fast with set -e. Testing: unit tests for functions, integration tests for workflows, test error cases, automate testing.
Understanding organization principles makes scripts maintainable, enables collaboration, simplifies debugging, and allows reuse across projects.