Menu
📱 Lihat versi lengkap (non-AMP)
Shell Scripting Linux Best Practices

Shell Scripting Best Practices: Error Handling dan Logging

Editor: Hendra WIjaya
Update: 3 February 2026
Baca: 5 menit

Shell Scripting Best Practices: Error Handling dan Logging

Shell scripting sering dianggap sebagai tools sederhana, namun untuk production environment diperlukan best practices yang ketat. Artikel ini membahas teknik error handling, logging, dan struktur script yang robust.

1. Shebang dan Header Standards

Shebang yang Tepat

#!/bin/bash
# Atau lebih portable:
#!/usr/bin/env bash

# Hindari:
#!/bin/sh  # Kurang portable, fitur terbatas

Script Header Template

#!/bin/bash

################################################################################
# Script Name: backup-script.sh
# Description: Automated backup script with rotation
# Author: Your Name
# Date: 2026-02-03
# Version: 1.0.0
# Usage: ./backup-script.sh [source_dir] [dest_dir]
# Dependencies: tar, gzip, logger
################################################################################

set -euo pipefail
IFS=$'\n\t'

2. Strict Mode dengan set Options

Set Options Penting

#!/bin/bash

# Exit immediately jika command gagal
set -e

# Exit jika ada undefined variable
set -u

# Exit jika pipeline gagal (bukan hanya command terakhir)
set -o pipefail

# Kombinasi (best practice)
set -euo pipefail

# Untuk debugging
set -x  # Print setiap command sebelum dieksekusi
set +x  # Stop debug mode

# Atau kombinasi lengkap:
set -euxo pipefail

Error Handling dengan Trap

#!/bin/bash
set -euo pipefail

# Cleanup function
cleanup() {
    local exit_code=$?
    echo "Script exiting dengan code: $exit_code"
    
    # Hapapus temporary files
    rm -f /tmp/script_*.tmp
    
    # Log exit
    logger -t backup-script "Script completed dengan exit code $exit_code"
    
    exit $exit_code
}

# Trap berbagai signals
trap cleanup EXIT
trap 'echo "Error di line $LINENO"' ERR
trap 'echo "Script interrupted"' INT TERM

# Main script logic here
main() {
    echo "Processing..."
    # Your code
}

main "$@"

3. Error Handling Robust

Function dengan Error Handling

#!/bin/bash
set -euo pipefail

# Function dengan proper error handling
backup_database() {
    local db_name="$1"
    local backup_dir="${2:-/backup}"
    
    # Validate parameters
    if [[ -z "$db_name" ]]; then
        log_error "Database name required"
        return 1
    fi
    
    # Check prerequisites
    if ! command -v mysqldump &> /dev/null; then
        log_error "mysqldump tidak ditemukan"
        return 1
    fi
    
    # Create backup directory
    if [[ ! -d "$backup_dir" ]]; then
        mkdir -p "$backup_dir" || {
            log_error "Gagal membuat direktori backup: $backup_dir"
            return 1
        }
    fi
    
    # Perform backup
    local backup_file="${backup_dir}/${db_name}_$(date +%Y%m%d_%H%M%S).sql"
    
    if mysqldump -u root -p"$DB_PASSWORD" "$db_name" > "$backup_file"; then
        log_info "Backup berhasil: $backup_file"
        echo "$backup_file"
        return 0
    else
        log_error "Backup gagal untuk database: $db_name"
        return 1
    fi
}

# Logging functions
log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2 | tee -a "$LOG_FILE" >&2
}

log_warn() {
    echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Usage
if backup_database "mydb" "/backup/db"; then
    log_info "Operation completed successfully"
else
    log_error "Operation failed"
    exit 1
fi

Command Substitution dengan Error Handling

#!/bin/bash
set -euo pipefail

# Simpan output dan exit code
if ! output=$(command_that_might_fail 2>&1); then
    echo "Command failed dengan output: $output" >&2
    exit 1
fi

# Atau dengan subshell
output=$(command_that_might_fail) || {
    echo "Command failed" >&2
    exit 1
}

# Multiple commands dengan error handling
{
    command1 &&
    command2 &&
    command3
} || {
    echo "One of the commands failed" >&2
    exit 1
}

4. Logging yang Professional

Sistem Logging Lengkap

#!/bin/bash

# Konfigurasi logging
readonly LOG_DIR="/var/log/myapp"
readonly LOG_FILE="$LOG_DIR/app-$(date +%Y-%m-%d).log"
readonly LOG_LEVEL="${LOG_LEVEL:-INFO}"

# Log levels
readonly LOG_LEVEL_DEBUG=0
readonly LOG_LEVEL_INFO=1
readonly LOG_LEVEL_WARN=2
readonly LOG_LEVEL_ERROR=3

# Setup logging directory
init_logging() {
    if [[ ! -d "$LOG_DIR" ]]; then
        mkdir -p "$LOG_DIR" || {
            echo "Failed to create log directory: $LOG_DIR" >&2
            exit 1
        }
    fi
}

# Logging functions
log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local script_name=$(basename "$0")
    local pid=$$
    
    echo "[$timestamp] [$script_name:$pid] [$level] $message" | tee -a "$LOG_FILE"
    
    # Also log ke syslog jika available
    if command -v logger &> /dev/null; then
        logger -t "$script_name" "[$level] $message"
    fi
}

log_debug() {
    [[ $LOG_LEVEL_DEBUG -ge $LOG_LEVEL ]] && log "DEBUG" "$@"
}

log_info() {
    [[ $LOG_LEVEL_INFO -ge $LOG_LEVEL ]] && log "INFO" "$@"
}

log_warn() {
    [[ $LOG_LEVEL_WARN -ge $LOG_LEVEL ]] && log "WARN" "$@"
}

log_error() {
    [[ $LOG_LEVEL_ERROR -ge $LOG_LEVEL ]] && log "ERROR" "$@"
}

# Rotation log
cleanup_old_logs() {
    find "$LOG_DIR" -name "app-*.log" -mtime +7 -delete
    log_info "Cleaned up old log files"
}

# Usage
init_logging
cleanup_old_logs

log_info "Starting backup process"
log_debug "Debug information: variable=$VAR"
log_warn "Disk space low: 85% used"
log_error "Connection failed to database"

5. Input Validation

Parameter Validation

#!/bin/bash
set -euo pipefail

validate_inputs() {
    # Check parameter count
    if [[ $# -lt 2 ]]; then
        echo "Usage: $0 <source_dir> <backup_dir>" >&2
        exit 1
    fi
    
    local source_dir="$1"
    local backup_dir="$2"
    
    # Validate directories exist
    if [[ ! -d "$source_dir" ]]; then
        echo "Error: Source directory tidak ditemukan: $source_dir" >&2
        exit 1
    fi
    
    # Validate not empty
    if [[ -z "$source_dir" ]]; then
        echo "Error: Source directory cannot be empty" >&2
        exit 1
    fi
    
    # Validate backup directory writable
    if [[ ! -w "$backup_dir" ]]; then
        echo "Error: Backup directory tidak writable: $backup_dir" >&2
        exit 1
    fi
    
    # Validate path tidak mengandung dangerous characters
    if [[ "$source_dir" =~ [\;\|\&\$\>\<] ]]; then
        echo "Error: Path mengandung karakter invalid" >&2
        exit 1
    fi
}

# Check required commands
check_dependencies() {
    local deps=("tar" "gzip" "logger")
    
    for dep in "${deps[@]}"; do
        if ! command -v "$dep" &> /dev/null; then
            echo "Error: Required command tidak ditemukan: $dep" >&2
            exit 1
        fi
    done
}

# Main
validate_inputs "$@"
check_dependencies

6. Configuration Management

Config File Loading

#!/bin/bash

# Load konfigurasi dari file
CONFIG_FILE="${CONFIG_FILE:-/etc/myapp/config.conf}"

# Default values
declare -A CONFIG
cat << 'EOF' > "${CONFIG_FILE}.example"
# Default configuration
BACKUP_DIR=/backup
RETENTION_DAYS=7
DB_HOST=localhost
DB_PORT=3306
DB_USER=backup_user
COMPRESS=true
LOG_LEVEL=INFO
EOF

load_config() {
    if [[ -f "$CONFIG_FILE" ]]; then
        # Source file dengan error handling
        if ! source "$CONFIG_FILE" 2>/dev/null; then
            echo "Warning: Failed to load config file: $CONFIG_FILE" >&2
            return 1
        fi
    else
        echo "Warning: Config file tidak ditemukan, menggunakan defaults" >&2
    fi
    
    # Set defaults jika tidak di-set
    BACKUP_DIR="${BACKUP_DIR:-/backup}"
    RETENTION_DAYS="${RETENTION_DAYS:-7}"
    DB_HOST="${DB_HOST:-localhost}"
    LOG_LEVEL="${LOG_LEVEL:-INFO}"
}

# Environment variable override
load_env_overrides() {
    [[ -n "${APP_BACKUP_DIR:-}" ]] && BACKUP_DIR="$APP_BACKUP_DIR"
    [[ -n "${APP_LOG_LEVEL:-}" ]] && LOG_LEVEL="$APP_LOG_LEVEL"
}

load_config
load_env_overrides

Kesimpulan

Shell scripting yang production-ready memerlukan error handling yang robust, logging yang comprehensive, dan validasi input yang ketat. Dengan mengikuti best practices ini, Anda dapat membuat script yang reliable dan maintainable.

Checklist Production Script:

  • Gunakan set -euo pipefail
  • Implementasikan proper logging
  • Validate semua input
  • Handle errors gracefully
  • Gunakan trap untuk cleanup
  • Document dengan komentar
  • Test dengan berbagai edge cases

Tools untuk Static Analysis:

  • shellcheck: Static analysis untuk bash scripts
  • bashate: Style checker untuk bash
  • shfmt: Auto-formatter untuk shell scripts
# Contoh penggunaan shellcheck
shellcheck your-script.sh

# Install shellcheck
sudo apt install shellcheck

Artikel Terkait

Bagikan:

Link Postingan: https://www.tirinfo.com/shell-scripting-best-practices-error-handling-logging/