Linux Bash Scripting untuk DevOps
Target: Web Developer & DevOps Engineer yang ingin mengotomasi tugas rutin server (seperti backup database, rotasi arsip, auto-deploy kode via Git, dan health-check sistem) menggunakan script shell Bash yang kuat, aman, dan tahan terhadap error. Versi: GNU Bash 5.1+ / 5.2+ (Ubuntu 22.04 LTS / 24.04 LTS). Fokus modul ini: Shebang portabel (
#!/usr/bin/env bash) → Strict Mode Defensif (set -euo pipefail) → Variabel, kutipan ("..."), dan argumen CLI ($1,$@,$#) → Kondisional modern ([[ ... ]]) & pengujian berkas (-f,-d,-z) → Looping & Fungsi → Logging terstruktur dengan timestamp → Penanganan error & sinyal trap (trap cleanup EXIT) → Mini project: Automated Database & Asset Backup Script.
Cara Belajar
🟢 Fundamental (Wajib Dipahami)
├── Anatomi Script Bash & Shebang Portabel (#!/usr/bin/env bash)
├── Hak Akses Eksekusi (chmod +x)
├── Variabel, Scope, dan Aturan Kutipan Ganda ("$VAR")
└── Argumen Baris Perintah ($1, $2, $@, $#) & Exit Status ($?)
🟡 Intermediate (Struktur Logika)
├── Strict Mode Defensif: set -euo pipefail (Mencegah Bug Bencana)
├── Evaluasi Kondisional Modern ([[ ... ]] vs [ ... ])
├── Operator Pengujian Berkas (-f file ada, -d folder ada, -z string kosong)
└── Looping Terstruktur & Pembuatan Fungsi Modular
🔴 Advanced / Otomasi Produksi
├── Exception Handling dengan Sinyal Trap (Pembersihan File Temp Otomatis)
├── Standardisasi Logging dengan Format Timestamp
└── Otomasi Skrip Mandiri yang Kompatibel dengan Cron DaemonMental Model: Mengapa Harus Defensive Bash?
Secara default, Bash memiliki perilaku yang sangat toleran terhadap kegagalan: jika sebuah baris perintah error, Bash akan tetap nekat mengeksekusi baris-baris berikutnya.
Perhatikan skenario bencana nyata berikut:
#!/bin/bash
# Skenario Bencana Tanpa Mode Defensif:
TARGET_DIR="/tmp/cache-lama"
# Misal terjadi typo pada nama variabel atau direktori target gagal di-set:
cd "$TARGET_DIRR" # Gagal berpindah direktori! Bash tetap lanjut mengeksekusi baris bawah!
rm -rf * # BENCANA BESAR: Menghapus seluruh isi direktori saat ini (misal /var/www)!Dengan mengaktifkan Bash Strict Mode (set -euo pipefail), Bash akan langsung menghentikan eksekusi script begitu mendeteksi variabel yang belum terdefinisi atau perintah yang menghasilkan status error.
Skenario Script Biasa Skenario Script Defensif (set -euo)
┌────────────────────────┐ ┌────────────────────────┐
│ Perintah 1 (Sukses) │ │ Perintah 1 (Sukses) │
└───────────┬────────────┘ └───────────┬────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ Perintah 2 (ERROR!) │ │ Perintah 2 (ERROR!) │
└───────────┬────────────┘ └───────────┬────────────┘
│ Bash tetap lanjut! │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ Perintah 3 (BENCANA) │ │ EKSEKUSI DIHENTIKAN! │
│ Data Rusak / Terhapus│ │ Sistem Aman & Terlindungi
└────────────────────────┘ └────────────────────────┘Daftar Isi
🟢 Fundamental
- Anatomi Script Bash & Shebang
- Defensive Bash Mode (set -euo pipefail)
- Variabel, Parameter CLI, & Exit Status
🟡 Intermediate
🔴 Advanced
🛠️ Praktik
📚 Referensi
1. 🟢 Anatomi Script Bash & Shebang
Konsep
Script Bash adalah berkas teks polos berisi rangkaian perintah shell yang dieksekusi secara berurutan. Baris pertama script harus selalu memuat Shebang (#!) yang memberi tahu kernel Linux interpreter mana yang harus digunakan.
Penulisan Shebang yang Portabel
#!/usr/bin/env bashTIP
Gunakan
#!/usr/bin/env bash, BUKAN#!/bin/bash! Di beberapa sistem operasi (seperti FreeBSD, macOS dengan Homebrew, atau container Alpine), biner Bash mungkin berada di/usr/local/bin/bash. Memanggil/usr/bin/env bashmenjamin script mencari biner Bash yang sesuai di environmentPATHsistem manapun.
Menjalankan Script
# 1. Berikan izin eksekusi pada file script
chmod +x deploy.sh
# 2. Eksekusi script secara langsung
./deploy.sh2. 🟢 Defensive Bash Mode (set -euo pipefail)
Konsep
Tambahkan baris berikut tepat di bawah Shebang di setiap script otomasi yang Anda buat:
#!/usr/bin/env bash
set -euo pipefailPenjelasan Rinci Setiap Flag
-e(errexit):- Menghentikan eksekusi script dengan segera jika ada satu perintah yang menghasilkan status keluar (exit status code) selain
0(gagal).
- Menghentikan eksekusi script dengan segera jika ada satu perintah yang menghasilkan status keluar (exit status code) selain
-u(nounset):- Menganggap pemanggilan variabel yang belum dideklarasikan sebagai error fatal dan langsung menghentikan script (mencegah typo nama variabel seperti
$DIRR).
- Menganggap pemanggilan variabel yang belum dideklarasikan sebagai error fatal dan langsung menghentikan script (mencegah typo nama variabel seperti
-o pipefail:- Secara bawaan, pada perintah pipeline
cmd1 | cmd2, Bash hanya mengecek status keluar dari perintah terakhir (cmd2). Opsipipefailmemastikan jikacmd1gagal, seluruh pipeline dianggap gagal.
- Secara bawaan, pada perintah pipeline
Jika Anda sengaja ingin menjalankan perintah yang diperbolehkan gagal tanpa menghentikan script, tambahkan || true:
# Perintah ini tidak akan memicu 'set -e' meskipun file tidak ditemukan
rm temporary-cache.tmp || true3. 🟢 Variabel, Parameter CLI, & Exit Status
Mendeklarasikan Variabel
#!/usr/bin/env bash
set -euo pipefail
# 1. Deklarasi (DILARANG memberi spasi di sekitar tanda sama dengan '=')
APP_NAME="Portal-Berita"
DEPLOY_PORT=8080
CURRENT_DATE=$(date +"%Y-%m-%d")
# 2. Mengakses nilai variabel (Selalu gunakan kurung kurawal & tanda kutip ganda)
echo "Deploying ${APP_NAME} pada tanggal: ${CURRENT_DATE}"IMPORTANT
Selalu Bungkus Variabel dengan Tanda Kutip Ganda (
"${VAR}")! Jika isi variabel memuat spasi (misalFILE_NAME="My Document.txt"), memanggilrm $FILE_NAMEtanpa kutip akan menyebabkan Bash memecahnya menjadi dua argumen:rm MydanDocument.txt(Word Splitting Bug).
Parameter Argumen Baris Perintah (CLI Arguments)
Saat menjalankan script dengan parameter (./backup.sh production database):
$0 → Nama berkas script itu sendiri (./backup.sh)
$1 → Argumen pertama ('production')
$2 → Argumen kedua ('database')
$# → Jumlah total argumen yang dikirim (2)
$@ → Seluruh argumen sebagai daftar terpisah ("production" "database")
$$ → Process ID (PID) dari script yang sedang berjalan
$? → Exit status code dari perintah terakhir (0 = Sukses, selain 0 = Error)Contoh Validasi Parameter Masukan:
#!/usr/bin/env bash
set -euo pipefail
# Periksa apakah argumen pertama diberikan
if [[ $#--lt-1-| -lt 1 ]]; then
echo "Penggunaan: $0 <target-environment>"
echo "Contoh: $0 production"
exit 1
fi
ENVIRONMENT="$1"
echo "Target deploy: ${ENVIRONMENT}"4. 🟡 Evaluasi Kondisional Modern & Uji Berkas
Sintaks [[ ... ]] vs [ ... ]
Gunakan tanda kurung siku ganda [[ ... ]] (standar bawaan Bash modern) daripada tanda kurung tunggal [ ... ] (standar POSIX lawas). Kurung ganda lebih aman dari error sintaks, mendukung operator logika && / ||, dan mendukung pencocokan regex.
Operator Pengujian Berkas & Folder
| Operator | Arti Evaluasi (True jika…) |
|---|---|
-f "$FILE" | File tersebut benar-benar ada dan merupakan file reguler |
-d "$DIR" | Direktori/folder tersebut benar-benar ada |
-z "$STR" | String bernilai kosong (Zero length) |
-n "$STR" | String memiliki isi (Not empty) |
-r "$FILE" | File dapat dibaca (Readable) |
-w "$FILE" | File dapat ditulis/diubah (Writable) |
-x "$FILE" | File dapat dieksekusi (Executable) |
Contoh Penggunaan:
CONFIG_FILE="/etc/nginx/nginx.conf"
if [[ -f "${CONFIG_FILE}" ]]; then
echo "Berkas konfigurasi ditemukan, melanjutkan pengujian..."
elif [[ -d "/etc/nginx" ]]; then
echo "Direktori nginx ada, tetapi file nginx.conf tidak ditemukan!"
exit 1
else
echo "NGINX belum terpasang di sistem!"
exit 1
fiPerbandingan Angka vs String
- String: Gunakan
==dan!=if [[ "${ROLE}" == "admin" ]]; then
- Angka (Numerik): Gunakan
-eq,-ne,-lt,-gt,-le,-geif [[ "${PORT}" -eq 80 ]]; then
5. 🟡 Looping, List Data, dan Fungsi
A. Looping Menggunakan Array / List
#!/usr/bin/env bash
set -euo pipefail
# Mendefinisikan array daftar nama service
SERVICES=("nginx" "mysql" "redis-server")
echo "=== Memeriksa Status Layanan ==="
for service in "${SERVICES[@]}"; do
if systemctl is-active --quiet "${service}"; then
echo "✅ Service ${service} sedang berjalan normal."
else
echo "❌ Service ${service} DALAM KONDISI MATI!"
fi
doneB. Membaca Berkas Baris Demi Baris
Gunakan konstruksi while IFS= read -r line (cara teraman membaca file teks tanpa merusak whitespace):
INPUT_FILE="/var/www/users.txt"
while IFS= read -r user || [[ -n "${user}" ]]; do
echo "Memproses user: ${user}"
done < "${INPUT_FILE}"C. Membuat Fungsi Modular
Fungsi di Bash membantu memecah script panjang menjadi bagian-bagian logis yang mudah dirawat.
#!/usr/bin/env bash
set -euo pipefail
# Mendefinisikan fungsi logging berstempel waktu
log_info() {
local message="$1"
echo "[INFO] [$(date +'%Y-%m-%d %H:%M:%S')] ${message}"
}
log_error() {
local message="$1"
# Cetak pesan error ke stderr (stream 2)
echo "[ERROR] [$(date +'%Y-%m-%d %H:%M:%S')] ${message}" >&2
}
# Memanggil fungsi
log_info "Memulai inisialisasi lingkungan aplikasi..."
log_error "Koneksi ke database gagal!"TIP
Selalu Gunakan Kata Kunci
localdi Dalam Fungsi! Secara default, seluruh variabel di Bash bersifat global. Mendeklarasikanlocal message="$1"mencegah fungsi mengubah nilai variabel di luar dirinya secara tidak sengaja.
6. 🔴 Error Handling, Traps, dan Logging Produksi
Konsep Pembersihan Otomatis dengan trap
Seringkali script otomasi membuat file temporer di /tmp. Jika script terhenti di tengah jalan karena error atau dihentikan paksa pengguna (Ctrl + C), file temporer tersebut akan tertinggal dan mengotori disk.
Dengan utilitas trap, kita dapat mendaftarkan fungsi pembersih (cleanup) yang dijamin akan dieksekusi saat script berhenti, baik saat selesai normal, terkena sinyal SIGINT, ataupun crash:
#!/usr/bin/env bash
set -euo pipefail
# Buat file kerja temporer
TEMP_FILE=$(mktemp /tmp/deploy-payload.XXXXXX)
# Fungsi pembersihan
cleanup() {
local exit_code=$?
echo "Membersihkan berkas temporer: ${TEMP_FILE}..."
rm -f "${TEMP_FILE}"
exit "${exit_code}"
}
# Daftarkan trap pada sinyal EXIT, SIGINT, dan SIGTERM
trap cleanup EXIT SIGINT SIGTERM
echo "Menulis data rahasia ke: ${TEMP_FILE}"
echo "Payload deployment aktif" > "${TEMP_FILE}"
# Lakukan proses kerja di sini...7. 🛠️ Mini Project: Script Otomasi Backup Database & Web Asset
Tujuan
Membangun script backup mandiri yang memenuhi standar produksi:
- Memakai strict mode
set -euo pipefail. - Melakukan dump database (simulasi SQLite/MySQL/PostgreSQL) dan arsip berkas web ke format
.tar.gz. - Menyimpan hasil backup dengan penamaan tanggal yang rapi.
- Menghapus backup lama yang berusia lebih dari 7 hari (rotasi disk otomatis).
- Menyediakan logging waktu dan pembersihan file temporer via
trap.
Implementasi Lengkap
Simpan script berikut di /usr/local/bin/auto-backup.sh:
#!/usr/bin/env bash
# ==============================================================================
# Script Otomasi Backup Harian Web Assets & Database
# ==============================================================================
set -euo pipefail
# ----------------- KONFIGURASI -----------------
SOURCE_DIR="/var/www/my-app/public"
BACKUP_ROOT="/var/backups/web-app"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
RETENTION_DAYS=7
BACKUP_ARCHIVE="${BACKUP_ROOT}/backup_${TIMESTAMP}.tar.gz"
TEMP_DIR=$(mktemp -d /tmp/backup-working.XXXXXX)
# ----------------- FUNGSI LOGGING -----------------
log() {
local level="$1"
local msg="$2"
echo "[${level}] [$(date +'%Y-%m-%d %H:%M:%S')] ${msg}"
}
# ----------------- TRAP CLEANUP -----------------
cleanup() {
local exit_status=$?
if [[ -d "${TEMP_DIR}" ]]; then
log "INFO" "Membersihkan folder temporer: ${TEMP_DIR}"
rm -rf "${TEMP_DIR}"
fi
if [[ ${exit_status} -eq 0 ]]; then
log "INFO" "=== Seluruh Operasi Backup Selesai dengan Sukses ==="
else
log "ERROR" "=== Backup GAGAL! Exit status: ${exit_status} ==="
fi
exit "${exit_status}"
}
trap cleanup EXIT SIGINT SIGTERM
# ----------------- PROSES UTAMA -----------------
log "INFO" "=== Memulai Alur Backup Harian ==="
# 1. Validasi keberadaan direktori sumber
if [[ ! -d "${SOURCE_DIR}" ]]; then
log "ERROR" "Direktori sumber ${SOURCE_DIR} tidak ditemukan!"
exit 1
fi
# 2. Buat direktori tujuan backup jika belum ada
mkdir -p "${BACKUP_ROOT}"
# 3. Simulasi dump data ke folder temporer
log "INFO" "Mengekstrak metadata database ke folder kerja..."
echo "Database dump exported at ${TIMESTAMP}" > "${TEMP_DIR}/db_dump.sql"
# 4. Menyalin aset statis ke folder kerja
log "INFO" "Menyalin berkas web dari ${SOURCE_DIR}..."
cp -r "${SOURCE_DIR}" "${TEMP_DIR}/assets"
# 5. Mengompresi hasil ke dalam arsip tar.gz
log "INFO" "Mengompresi data backup ke ${BACKUP_ARCHIVE}..."
tar -czf "${BACKUP_ARCHIVE}" -C "${TEMP_DIR}" .
# 6. Menampilkan ukuran arsip akhir
ARCHIVE_SIZE=$(du -h "${BACKUP_ARCHIVE}" | awk '{print $1}')
log "INFO" "Arsip backup berhasil dibuat! Ukuran berkas: ${ARCHIVE_SIZE}"
# 7. Rotasi Otomatis: Hapus backup yang lebih tua dari batas retensi
log "INFO" "Memeriksa dan menghapus backup yang berumur > ${RETENTION_DAYS} hari..."
find "${BACKUP_ROOT}" -type f -name "backup_*.tar.gz" -mtime +"${RETENTION_DAYS}" -exec rm -f {} \; -printMenguji dan Mengintegrasikan dengan Cron
# 1. Berikan hak eksekusi pada script
sudo chmod +x /usr/local/bin/auto-backup.sh
# 2. Uji jalankan langsung secara manual
sudo /usr/local/bin/auto-backup.sh
# 3. Daftarkan ke Crontab root agar berjalan otomatis setiap hari pukul 02.00 pagi
# Buka editor crontab
sudo crontab -e
# Tambahkan baris:
0 2 * * * /usr/local/bin/auto-backup.sh >> /var/log/backup-cron.log 2>&18. 📚 Ringkasan & Peta Ingatan
Peta Ingatan Konsep
Bash Scripting untuk DevOps
├── Fondasi Defensif
│ ├── Shebang portabel → #!/usr/bin/env bash
│ └── set -euo pipefail → Error exit, unset check, pipefail
├── Variabel & Argumen
│ ├── Double Quotes → "${VAR}" (cegah word splitting)
│ ├── $1, $2, $@, $# → Argumen baris perintah
│ └── Exit Code ($?) → 0 sukses, selain 0 error
├── Kontrol Alur
│ ├── [[ ... ]] → Evaluasi kondisional modern
│ ├── -f, -d, -z → Uji file, folder, dan string kosong
│ └── for & while read → Iterasi list dan membaca berkas
└── Operasional Produksi
├── Functions → local var="$1"
├── trap cleanup EXIT → Bersihkan file temp otomatis
└── Cron integration → Eksekusi berkala tanpa pengawasanCheat Code 10 Detik
set -euo pipefail → aktifkan strict mode anti-bug
chmod +x script.sh → jadikan script executable
if [[ -f "$FILE" ]]; then → periksa apakah file ada
trap "rm -f $TMP" EXIT → pembersihan file otomatis saat script selesai
TIMESTAMP=$(date +"%Y%m%d") → generate string stempel tanggal
for item in "${ARR[@]}"; do → looping seluruh elemen array9. 🧭 Urutan Belajar Berikutnya
- Otomasi CI/CD: Terapkan pemahaman Bash script ini pada workflow otomatisasi pengujian kode di Git Workflow & Kolaborasi.
- Container Entrypoint: Tulis skrip
entrypoint.shkustom untuk menginisialisasi environment container di Dockerfile Dasar. - Penyempurnaan Server: Pelajari bagaimana memadukan script backup dengan basis data skala besar di PostgreSQL Fungsi & Administrasi.