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 pipefailCleanup 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 TERMMain script logic here
main() {
echo "Processing..."Your code
}
main "$@"
3. Error Handling Robust
Function dengan Error Handling
#!/bin/bash set -euo pipefailFunction 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
fiCommand Substitution dengan Error Handling
#!/bin/bash set -euo pipefailSimpan 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/bashKonfigurasi 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_logslog_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 pipefailvalidate_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_dependencies6. Configuration Management
Config File Loading
#!/bin/bashLoad 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_overridesKesimpulan
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:
– Gunakanset -euo pipefail
– Implementasikan proper logging
– Validate semua input
– Handle errors gracefully
– Gunakan trap untuk cleanup
– Document dengan komentar
– Test dengan berbagai edge casesTools 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.shInstall shellcheck
sudo apt install shellcheck
Ditulis oleh
Hendra Wijaya