Topik ini merancang proyek cadangkan-catatan.sh sebelum kode utuhnya ditulis, mencakup tiga keputusan yang menentukan mutu sebuah skrip, yaitu antarmuka baris perintah, mode kegagalan, dan strategi logging. Setiap keputusan diuji lebih dahulu lewat purwarupa, yakni potongan kecil yang dijalankan sendiri untuk membuktikan perilakunya, sehingga pengerjaan pada topik implementasi proyek cukup merakit bagian yang sudah teruji. Setelah membaca topik ini, alasan di balik setiap opsi, pesan error, dan baris log skrip proyek dapat dijelaskan, bukan sekadar ditiru.
Antarmuka baris perintah#
Antarmuka proyek ini terdiri atas empat opsi yang diuraikan dengan getopts sebagaimana pada topik argumen dan flag. Opsi -s menyebut direktori sumber dan bersifat wajib karena tanpanya skrip tidak memiliki pekerjaan. Opsi -d menyebut direktori tujuan dengan nilai bawaan cadangan, sedangkan opsi -n menyebut jumlah maksimum cadangan dengan nilai bawaan 3. Opsi -h mencetak pesan penggunaan, yaitu satu baris ringkas yang menunjukkan cara memanggil skrip.
# purwarupa pesan penggunaan: dipanggil pada opsi -h dan pada setiap error opsi
penggunaan() {
printf 'Penggunaan: %s -s direktori_sumber [-d direktori_cadangan] [-n jumlah_maksimum] [-h]\n' "cadangkan-catatan.sh"
}
penggunaanPenggunaan: cadangkan-catatan.sh -s direktori_sumber [-d direktori_cadangan] [-n jumlah_maksimum] [-h]Nama skrip pada purwarupa ini ditulis langsung, sedangkan skrip utuh memakai $0 yang berisi nama skrip sebagaimana dipanggil. Nilai bawaan pada -d dan -n memang sengaja dipilih, karena pemanggilan harian cukup menuliskan satu opsi -s, sedangkan nilai yang berbeda tetap dapat diberikan saat dibutuhkan. Penguraian opsi sendiri diuji dengan purwarupa berikut yang berjalan di dalam subshell, yaitu shell anak yang tidak mengubah keadaan sesi di sekitarnya, sehingga argumen latihan tidak bocor ke perintah lain.
# purwarupa penguraian opsi dengan getopts di dalam subshell
(
set -- -s catatan -n 3
while getopts ":s:n:h" opt; do
case "$opt" in
s) printf 'sumber = %s\n' "$OPTARG" ;;
n) printf 'maksimum = %s\n' "$OPTARG" ;;
esac
done
)sumber = catatan
maksimum = 3Rincian kecil pada purwarupa tersebut mencerminkan keputusan yang nyata. Titik dua di awal string opsi, yaitu :s:n:h, menyalakan mode senyap getopts, sehingga opsi yang tidak dikenal tidak langsung dicetak oleh getopts, melainkan diserahkan ke cabang \? untuk diberi pesan yang konsisten dengan gaya skrip. Huruf opsinya pun dipilih yang mudah diingat, yaitu s untuk sumber, d untuk tujuan, n untuk jumlah maksimum, dan h untuk bantuan.
Mode kegagalan#
Mode kegagalan adalah daftar keadaan salah yang diperkirakan beserta respons skripnya, dan daftar ini disusun sebelum kode ditulis agar responsnya konsisten. Prinsipnya satu, yaitu berhenti secepatnya dengan pesan yang jelas ke stderr dan kode keluar bukan nol, sehingga pemanggil maupun skrip lain dapat mendeteksi kegagalan. Tabel berikut merangkum lima mode kegagalan yang dirancang untuk proyek ini beserta kode keluarnya.
| Kondisi | Respons | Kode keluar |
|---|---|---|
Opsi -s tidak diberikan | pesan error beserta pesan penggunaan | 2 |
| Opsi tidak dikenal atau kekurangan nilai | pesan error beserta pesan penggunaan | 2 |
Nilai -n bukan bilangan bulat positif | pesan error tanpa pesan penggunaan | 2 |
| Direktori sumber tidak ada | pesan error | 1 |
Perintah tar gagal | diteruskan oleh strict mode, skrip berhenti | kode keluar tar |
Validasi nilai -n dipisahkan menjadi fungsi agar dapat diuji terpisah sebelum digabung, sebagaimana dua pemanggilan berikut. Nilai yang tidak sah menghasilkan pesan error dan kode keluar 2, sedangkan nilai yang sah lolos tanpa pesan apa pun.
# purwarupa validasi nilai opsi -n sebagai fungsi yang dapat diuji terpisah
validasi_angka() {
case "$1" in
''|*[!0-9]*)
printf 'Error: -n harus berupa bilangan bulat positif, diterima: %s\n' "$1" >&2
return 2
;;
esac
}
validasi_angka banyak
printf 'kode keluar: %s\n' "$?"Error: -n harus berupa bilangan bulat positif, diterima: banyak
kode keluar: 2# nilai yang sah lolos validasi tanpa pesan apa pun
validasi_angka 3
printf 'kode keluar: %s\n' "$?"kode keluar: 0Baris terakhir tabel menunjukkan keuntungan strict mode yang sudah dibahas pada topik strict mode, yaitu kegagalan perintah mana pun di tengah skrip langsung menghentikan skrip tanpa perlu pemeriksaan manual satu per satu. Rancangan ini juga menetapkan bahwa kegagalan validasi masukan memakai kode keluar 2, sedangkan kegagalan pekerjaan sesungguhnya memakai kode keluar 1, sehingga dua jenis kegagalan itu dapat dibedakan oleh pemanggilnya.
Strategi logging#
Strategi logging proyek ini menetapkan tiga aturan yang membuat keluarannya mudah dibaca dan mudah diuji ulang. Pertama, setiap kejadian penting dicetak sebagai satu baris dengan fungsi log berawalan [INFO], sehingga baris log dapat dihitung dan dicari. Kedua, pesan error dicetak ke stderr melalui fungsi gagal berawalan [ERROR], sehingga redirection 2>&1 atau pemisahan arus tetap dapat mengatur keduanya. Ketiga, baris log tidak memuat timestamp agar keluaran skrip identik pada setiap pengujian, sebab timestamp justru menyulitkan perbandingan keluaran, sedangkan pencatatan berwaktu dapat ditangani oleh penjadwal seperti cron saat skrip dipasangkan dengannya.
# fungsi log: satu baris per kejadian dengan awalan [INFO]
log() {
printf '%s\n' "[INFO] $*"
}
log "sumber: catatan"
log "tujuan: cadangan"[INFO] sumber: catatan
[INFO] tujuan: cadangan# fungsi error menulis ke stderr agar tidak tercampur keluaran normal
gagal() {
printf '%s\n' "[ERROR] $*" >&2
}
gagal "direktori sumber tidak ada: catatan-lama"[ERROR] direktori sumber tidak ada: catatan-lamaPemisahan arus pada dua fungsi itu memiliki manfaat praktis yang langsung terasa. Keluaran standar tetap murni berisi baris log kejadian, sehingga barisnya dapat dihitung, disaring dengan grep, atau diarahkan ke berkas lewat redirection tanpa tercampur pesan error. Baris log juga dicetak mengikuti urutan eksekusi, sehingga membaca log dari atas ke bawah sama dengan mengulang jalannya satu pemanggilan.
Keputusan rotasi juga dicatat ke log karena menghapus berkas adalah tindakan yang tidak dapat dibatalkan, sehingga purwarupa keputusannya diuji di sini dengan memanfaatkan fungsi log yang didefinisikan lebih dahulu pada bagian ini. Daftar arsip diurutkan menaik, sehingga elemen pertamanya adalah arsip tertua yang menjadi sasaran rotasi pertama.
# purwarupa keputusan rotasi: kuota tiga, arsip baru membuat jumlah menjadi empat
arsip=(cadangan-001.tar.gz cadangan-002.tar.gz cadangan-003.tar.gz)
maks=3
jumlah_baru=$(( ${#arsip[@]} + 1 ))
if [ "$jumlah_baru" -gt "$maks" ]; then
log "rotasi: arsip tertua ${arsip[0]} akan dihapus"
else
log "kuota cadangan masih tersisa $(( maks - jumlah_baru ))"
fi[INFO] rotasi: arsip tertua cadangan-001.tar.gz akan dihapusPenamaan arsip bernomor#
Nama arsip dirancang berurutan seperti cadangan-001.tar.gz dan bukan memuat timestamp, dengan dua alasan yang saling menguatkan. Urutan nama menjadi urutan waktu, sehingga sort dan ls -1 langsung menampilkan riwayat yang benar tanpa pengaturan tambahan. Keluaran skrip juga menjadi deterministik, yakni sama pada setiap kali skrip dijalankan, sehingga pengujian ulang atas artikel ini dapat membandingkan keluaran secara persis. Penomoran memakai printf dengan konversi %03d agar nomor satu digit maupun dua digit memiliki lebar yang sama.
# penomoran dengan nol di depan agar urutan sort sesuai urutan waktu
printf -v nama_arsip 'cadangan-%03d.tar.gz' 1
printf '%s\n' "$nama_arsip"
printf -v nama_arsip 'cadangan-%03d.tar.gz' 12
printf '%s\n' "$nama_arsip"cadangan-001.tar.gz
cadangan-012.tar.gzAda satu jebakan yang wajib diantisipasi ketika nomor tersebut dibaca kembali dari nama berkas, yaitu bash menafsirkan angka berawalan nol sebagai bilangan oktal di dalam ekspansi aritmetika. Nilai 010 yang dimaksudkan sebagai sepuluh terbaca sebagai delapan, sehingga konversi 10# dipakai untuk memaksa pembacaan desimal.
# angka berawalan nol dibaca sebagai oktal, konversi 10# memaksa desimal
angka=010
printf 'tanpa 10#: %s\n' "$((angka))"
printf 'dengan 10#: %s\n' "$((10#$angka))"tanpa 10#: 8
dengan 10#: 10Rumus nomor berikutnya juga perlu diputuskan sejak rancangan. Menomori dari hitungan arsip terlihat sederhana, tetapi hasilnya salah setelah rotasi bekerja, karena hitungan yang menyusut membuat nomor baru menabrak nama arsip yang masih ada dan arsip lama tertimpa. Aturan yang dipakai adalah mengambil nomor tertinggi yang sah dari daftar terurut lalu menambah satu, sehingga deret tidak pernah mundur berapa pun arsip yang telah terhapus. Dengan lebar tiga digit, skema ini cukup untuk 999 cadangan sebelum lebarnya bertambah sendiri.
Alur keputusan skrip#
Seluruh keputusan rancangan di atas dirangkum menjadi satu alur yang dipakai sebagai peta saat implementasi. Diagram berikut memperlihatkan jalur utama dari pembacaan opsi hingga pencatatan log, beserta dua percabangan keputusannya, yaitu validasi opsi dan keputusan rotasi.
Warna kotak pada diagram membedakan jenis langkahnya, yaitu hijau untuk mulai dan selesai, biru untuk langkah kerja, jingga untuk keputusan, dan merah untuk keluar karena error. Dua kotak keputusan mewakili dua rancangan di atas, yaitu Opsi valid? terhadap tabel mode kegagalan dan Melebihi batas? terhadap purwarupa rotasi. Dengan pemetaan itu, diagram berfungsi sebagai peta proyek yang dapat dibaca sejajar dengan kode.
Jalur kiri bawah pada diagram adalah penerapan prinsip berhenti secepatnya, yaitu opsi yang tidak valid langsung berakhir dengan pesan error tanpa menyentuh berkas apa pun. Jalur kanan bawah memperlihatkan bahwa rotasi hanya terjadi apabila jumlah cadangan melebihi batas, sedangkan keadaan normal menuju pencatatan log tanpa penghapusan. Urutan kotak pada diagram pula yang menjadi urutan bagian skrip pada topik implementasi proyek, sehingga peta dan kode dapat dibaca sejajar.
Ringkasan#
- Antarmuka proyek terdiri atas opsi wajib
-s, opsi-ddan-ndengan nilai bawaan, serta-huntuk pesan penggunaan. - Setiap mode kegagalan berhenti secepatnya dengan pesan ke stderr dan kode keluar bukan nol, dibedakan antara kegagalan masukan dan kegagalan pekerjaan.
- Logging memakai awalan
[INFO]dan[ERROR]tanpa timestamp agar keluaran deterministik dan mudah diuji ulang. - Arsip diberi nama bernomor dengan konversi
%03ddan konversi10#untuk menghindari pembacaan oktal.
Referensi#
- GNU Bash Reference Manual — Free Software Foundation — Manual resmi bash untuk getopts, printf, ekspansi aritmetika, dan kode keluar.
- ShellCheck — shell script analysis tool — Alat pemeriksa skrip shell yang menjadi standar mutu proyek ini.
- Google Shell Style Guide — Google — Panduan gaya untuk struktur fungsi, keluaran, dan penanganan kegagalan skrip.