Lewati ke konten
Kembali ke Blog

Shell Scripting Best Practices: Error Handling dan Logging

· · 7 menit baca

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 &quot;$CONFIG_FILE&quot; 2&gt;/dev/null; then
        echo &quot;Warning: Failed to load config file: $CONFIG_FILE&quot; &gt;&amp;2
        return 1
    fi
else
    echo &quot;Warning: Config file tidak ditemukan, menggunakan defaults&quot; &gt;&amp;2
fi

# Set defaults jika tidak di-set
BACKUP_DIR=&quot;${BACKUP_DIR:-/backup}&quot;
RETENTION_DAYS=&quot;${RETENTION_DAYS:-7}&quot;
DB_HOST=&quot;${DB_HOST:-localhost}&quot;
LOG_LEVEL=&quot;${LOG_LEVEL:-INFO}&quot;

}

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

Ditulis oleh

Hendra Wijaya

Tinggalkan Komentar

Email tidak akan ditampilkan.