Defensive Scripting & trap — Terminal

Defensive Scripting & trap Script shell yang rapuh bisa menghancurkan data, meninggalkan file setengah jadi, atau berjalan salah diam-diam. Shell tidak membantu

Defensive Scripting & trap

Script shell yang rapuh bisa menghancurkan data, meninggalkan file setengah jadi, atau berjalan salah diam-diam. Shell tidak membantu secara default — kamu harus nyala-kan safety-nya sendiri.

set -e Saja Tidak Cukup

Banyak tutorial menyarankan set -e di awal script. Berguna tapi punya banyak celah:

#!/bin/bash
set -e

grep "ERROR" log.txt | wc -l    # Kalau grep tidak ketemu apa-apa,
                                 # exit code grep 1 → set -e berhenti?
                                 # TIDAK — exit code yang dilihat adalah wc.

Solusinya pasang tiga bendera bersamaan — ini preamble defensif yang dipakai script profesional:

#!/bin/bash
set -euo pipefail
# (tambahkan: IFS=
    
    
    
    
    
    
    
  

\n\t' untuk word-splitting yang aman)

-e — Exit on Error

Script berhenti jika ada perintah yang gagal (exit non-zero):

set -e
rm important.txt        # kalau gagal → berhenti
echo "Lanjut..."        # tidak dijalankan

Gotcha: -e tidak aktif di dalam if, while, &&, ||. Ini desain, supaya cek kondisi tidak langsung exit.

-u — Undefined Variable Fails

Akses variabel yang belum di-set jadi error:

set -u
echo "User: $USERNAME"   # kalau USERNAME tidak ada → error + exit

# Tanpa -u, $USERNAME = "" diam-diam — potensi bug diam
rm -rf "$TARGETDIR/cache"    # kalau $TARGETDIR kosong → rm -rf "/cache"!

-o pipefail — Pipe Failures Caught

Default: hanya exit code perintah terakhir di pipeline yang dipakai. pipefail ubah: any gagal → pipeline gagal.

set -o pipefail
grep "error" missing.txt | wc -l
# Tanpa pipefail: wc berhasil (0 lines) → exit 0 ✓ (salah!)
# Dengan pipefail: grep gagal → pipeline exit 1 ✓ (benar)

-x — Debug Trace

Cetak setiap perintah sebelum dijalankan — tool debug paling efektif:

set -x
echo "hello"
# + echo hello
# hello

Aktifkan sementara:

set -x
perintah-yang-bermasalah
set +x   # matikan kembali

Atau cek sekali via bash -x script.sh.

Parameter Expansion Defaults

Cara expected menangani variabel yang mungkin kosong:

${VAR:-default}       # jika VAR kosong/unset → "default", tidak ubah VAR
${VAR:=default}       # jika kosong → set VAR ke "default"
${VAR:?pesan error}   # jika kosong → exit dengan pesan (cocok untuk validasi)
${VAR:+alt}           # jika VAR ter-set → return "alt", jika kosong → ""
# Validasi argumen wajib
: "${DB_HOST:?DB_HOST harus di-set}"

# Default value
PORT="${PORT:-3000}"

# Optional flag
${VERBOSE:+--verbose}

trap — Handler untuk Signal & Exit

trap menjalankan perintah saat signal diterima atau script exit. Ini yang memungkinkan cleanup garansi, graceful shutdown, dan error hook.

Cleanup Garansi dengan EXIT

#!/bin/bash
set -euo pipefail

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# Folder temp DIJAMIN terhapus — baik script sukses, gagal, atau Ctrl+C

# kerjakan sesuatu di $TMPDIR
echo "data" > "$TMPDIR/work.txt"
# (jika script crash di sini, rm -rf tetap jalan)

Graceful Shutdown dengan INT/TERM

cleanup() {
    echo ""
    echo "Mematikan server dengan sopan..."
    kill -TERM "$SERVER_PID" 2>/dev/null
    wait "$SERVER_PID"
    echo "Selesai."
}
trap cleanup INT TERM

./server &
SERVER_PID=$!
wait "$SERVER_PID"
# Ctrl+C → panggil cleanup, bukan langsung mati

Error Hook dengan ERR

on_error() {
    echo "ERROR di baris $1, exit code $2" >&2
    # kirim ke logging service, Slack, dll
}
trap 'on_error $LINENO $?' ERR

apa-yang-gagal
# → ERROR di baris 5, exit code 127

Signal Umum untuk trap

Signal Kapan Contoh penggunaan
EXIT Script selesai (alasan apa pun) Cleanup file temp
ERR Ada perintah gagal (butuh -e) Log error + notify
INT (2) Ctrl+C Graceful shutdown
TERM (15) kill tanpa -9 Shutdown service
HUP (1) Terminal tertutup / reload Reload config

Hapus trap: trap - EXIT.

shellcheck — Linter untuk Shell

Tool wajib untuk script shell. Menangkap bug umum sebelum script dijalankan:

brew install shellcheck          # macOS
sudo apt install shellcheck      # Ubuntu

shellcheck deploy.sh
# deploy.sh:5:7: warning: Quote this to prevent word splitting. [SC2086]

Integrasikan di editor (VS Code, Vim, Neovim) untuk feedback real-time.

Template Script Defensif

Gunakan ini sebagai starting point:

#!/usr/bin/env bash
#
# deploy.sh — Deploy aplikasi ke staging
# Usage: ./deploy.sh <version>

set -euo pipefail
IFS=
    
    
    
    
    
    
    
  

\n\t'

# Validasi argumen
: "${1:?Usage: $0 <version>}"
VERSION="$1"

# Cleanup garansi
WORKDIR=$(mktemp -d)
trap 'rm -rf "$WORKDIR"' EXIT

# Error hook
trap 'echo "ERROR di baris $LINENO" >&2' ERR

# Cek prerequisite
command -v docker >/dev/null || { echo "docker tidak ditemukan" >&2; exit 1; }

echo "Deploying $VERSION ke $WORKDIR..."
# ... kerja di sini ...
echo "Selesai."

Tips

  1. Pasang set -euo pipefail di setiap script — jadikan habit.
  2. Test dengan bash -x saat debugging — lihat setiap langkah.
  3. trap "..." EXIT untuk cleanup, jangan andalkan script sukses.
  4. Validasi argumen dengan ${VAR:?...} di awal — fail fast.
  5. Lint dengan shellcheck sebelum commit.

Yang akan kamu pelajari