Topik ini membahas pola penanganan error yang digunakan skrip Perl ketika jalannya program tidak sesuai rencana. Pembahasan mencakup pelaporan kegagalan dengan die, peringatan dengan warn, penyertaan pesan error sistem operasi melalui variabel $!, penangkapan error dengan blok eval, serta blok try dan catch dari modul Try::Tiny. Setelah topik ini diikuti, kegagalan dapat dilaporkan dengan pesan yang jelas, dan program dapat memutuskan untuk berhenti atau berlanjut secara sadar.
Menghentikan program dengan die#
Fungsi die mencetak pesan ke aliran STDERR, lalu menghentikan program dengan status keluar bukan nol. Program lain yang memanggil skrip tersebut, termasuk shell, dapat membaca status itu untuk mengetahui bahwa pekerjaan tidak selesai. Karena hanya menerima satu pesan, isi pesan die ditulis sedetail mungkin, yaitu apa yang gagal dan nilai yang menyebabkannya.
#!/usr/bin/perl
use strict;
use warnings;
# data wajib sah sebelum pemrosesan dimulai
my $jumlah_sampel = 0;
die "Jumlah sampel tidak boleh nol\n" if $jumlah_sampel == 0;
print "Memproses $jumlah_sampel sampel\n";Jumlah sampel tidak boleh nolTanda \n pada akhir pesan menentukan bentuk keluarannya. Apabila pesan berakhir dengan baris baru, die mencetaknya apa adanya, sedangkan apabila tidak, die menambahkan lokasi baris tempatnya dipanggil. Perilaku itu terlihat pada skrip berikut yang sengaja menghilangkan tanda baris baru.
# tidak-dijalankan: keluarannya memuat nama skrip, peragaannya ada pada blok bash di bawah
use strict;
use warnings;
# pesan tanpa baris baru di akhirnya
die "Berkas log-suhu.txt tidak ditemukan";Karena pesannya memuat lokasi baris, skrip ini diperagakan dari direktori tempat berkasnya berada agar nama yang tertulis pada pesan sesuai dengan nama berkasnya:
# menjalankan die-lokasi.pl dari direktori berkasnya
perl die-lokasi.pl 2>&1Berkas log-suhu.txt tidak ditemukan at die-lokasi.pl line 6.Lokasi baris seperti itu berguna ketika menguji, tetapi kurang berarti bagi pengguna akhir. Kebiasaan yang digunakan sepanjang jalur ini adalah menutup pesan die dengan \n secara sadar, baik karena pesannya sudah lengkap maupun karena lokasi barisnya memang diinginkan. Status keluar skrip pertama adalah 255, yaitu nilai yang digunakan apabila tidak ada nomor error sistem operasi yang menyertai kegagalannya.
Pesan error sistem dengan $!#
Apabila kegagalannya berasal dari sistem operasi, misalnya berkas yang tidak ada, pesan yang siap pakai tersedia di variabel khusus $!. Variabel ini berisi teks error dari operasi sistem yang terakhir gagal, sehingga pesan die cukup menyambungkannya di belakang keterangan singkat. Pola open or die yang digunakan sepanjang modul Berkas dan I/O menyatukan keduanya dalam satu baris.
#!/usr/bin/perl
use strict;
use warnings;
# pola open or die dengan pesan error dari $!
open(my $baca, '<', 'log-suhu.txt')
or die "Tidak dapat membuka log-suhu.txt: $!\n";
while (my $baris = <$baca>) {
print $baris;
}
close($baca);Tidak dapat membuka log-suhu.txt: No such file or directoryPesan hasilnya menyebutkan penyebab teknis secara persis, yaitu No such file or directory, sehingga penyebab kegagalannya tidak perlu ditebak. Pesan ditutup dengan \n secara sadar karena nama berkas yang gagal sudah disebut lengkap di dalam pesannya, sehingga tambahan lokasi baris tidak menambah informasi apa pun. Status keluar skrip adalah 2, yaitu nomor error sistem operasi yang tersimpan di $! ketika die mengakhirinya.
Variabel $! memuat dua bentuk nilai sekaligus, yaitu nomor error dalam konteks angka dan teks pesannya dalam konteks string. Pencetakan biasa menampilkan teksnya, sedangkan operasi angka menampilkan nomor errornya. Modul inti Errno menyediakan hash %! yang memetakan nama error menjadi nilai kebenarannya, sehingga pemeriksaan $!{ENOENT} menunjukkan apakah berkasnya benar-benar tidak ada tanpa mencocokkan teks pesan.
Nilai $! hanya bermakna segera setelah pemanggilan sistem yang gagal. Pemanggilan berikutnya yang berhasil tidak mengosongkannya, sehingga isi lama tetap terbaca seolah-olah merupakan error terbaru. Karena itu, nilai balik open diperiksa terlebih dahulu, dan $! baru dibaca apabila nilai baliknya menunjukkan kegagalan.
Peringatan dengan warn#
Fungsi warn mencetak pesan ke STDERR tanpa menghentikan program. Bentuknya mirip die, tetapi pernyataan berikutnya tetap dijalankan, sehingga warn cocok untuk kejadian yang mencurigakan namun masih dapat dilewati. Pesan peringatan tetap masuk aliran error, sehingga tetap terpisah dari keluaran data ketika keduanya diarahkan ke berkas yang berbeda.
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
my @suhu = (24.1, 0, 25.3);
foreach my $nilai (@suhu) {
if ($nilai == 0) {
warn "Suhu 0 pada posisi ke-2 diabaikan\n";
next;
}
print "Suhu tercatat: $nilai\n";
}
print "Pemrosesan selesai\n";Suhu tercatat: 24.1
Suhu 0 pada posisi ke-2 diabaikan
Suhu tercatat: 25.3
Pemrosesan selesaiBaris $| = 1; menonaktifkan penyanggaan keluaran standar. Tanpa baris itu, keluaran yang dikirim ke saluran yang disangga diteruskan per blok, sedangkan pesan warn langsung tampil, sehingga urutan baris pada keluaran gabungan dapat tertukar. Dengan penyanggaan yang dinonaktifkan, urutan baris selalu mengikuti urutan kode. Perhatikan pula bahwa baris Pemrosesan selesai tetap dicetak meskipun ada satu data yang diabaikan, yang merupakan perbedaan mendasar dibandingkan dengan die.
Perilaku penambahan lokasi baris pada warn mengikuti die, yaitu pesan tanpa baris baru di akhirnya dilengkapi nama berkas dan nomor baris tempatnya dipanggil. Apabila dipanggil tanpa argumen, warn menggunakan isi $_ sebagai pesannya. Argumen berupa string kosong menghasilkan pesan bawaan Warning: something's wrong. Karena itu, kebiasaan menutup pesan dengan baris baru berlaku untuk warn sebagaimana untuk die.
Aliran pesan peringatan dapat dialihkan dengan memasang penangan pada $SIG{__WARN__}. Penangan tersebut menerima teks peringatan sebagai argumennya, dan jalur yang sama juga digunakan oleh peringatan yang dihasilkan use warnings. Dengan penangan semacam itu, peringatan dapat dicatat ke berkas log tanpa mencampur keluaran data.
Menangkap error dengan eval#
Blok eval menjalankan kode dengan errornya tertahan. Apabila di dalam blok terjadi error fatal, misalnya pembagian dengan nol, eval mengembalikan nilai tak tentu dan menyimpan pesan errornya di variabel $@. Apabila tidak ada error, $@ berisi string kosong dan nilai balik eval adalah nilai terakhir bloknya. Bentuk ini disebut eval blok, dan perannya menyerupai try dan catch pada bahasa lain.
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
my @pembagi = (4, 0, 5);
foreach my $p (@pembagi) {
my $hasil = eval { 100 / $p };
if ($@) {
chomp(my $pesan = $@);
$pesan =~ s/ at .*? line \d+\.$//;
print "Gagal untuk pembagi $p: $pesan\n";
}
else {
print "100 / $p = $hasil\n";
}
}
print "Perhitungan selesai\n";100 / 4 = 25
Gagal untuk pembagi 0: Illegal division by zero
100 / 5 = 20
Perhitungan selesaiPesan yang tersimpan dalam $@ memuat lokasi baris pembagiannya secara utuh. Sebelum dicetak, pola s/// memangkas lokasi baris itu dari pesan, sehingga yang tampil kepada pengguna hanya penyebabnya, sedangkan pesan aslinya tetap tersedia apabila lokasi barisnya diperlukan saat menguji. Program berlanjut ke pembagi berikutnya dan diakhiri baris Perhitungan selesai, yang menunjukkan bahwa error tidak lagi menghentikan seluruh skrip. Untuk error yang dibangkitkan sendiri, die di dalam blok eval bekerja dengan cara yang sama, yaitu pesannya tertangkap dan programnya tetap berjalan.
Try::Tiny untuk blok try yang bersih#
Modul Try::Tiny menyediakan blok try, catch, dan finally dengan tanda kurung kurawal yang terbaca seperti bahasa lain. Pesan error tersedia di $_ di dalam blok catch, sedangkan blok finally dijalankan baik terjadi error maupun tidak, sehingga cocok untuk pembersihan sumber daya. Modul ini berasal dari CPAN dan dipasang dengan cpanm sebagaimana dibahas pada topik CPAN.
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
use Try::Tiny;
try {
die "berat sampel melebihi batas\n";
}
catch {
print "Tertangkap: $_";
}
finally {
print "Pembersihan berjalan dalam kondisi apa pun\n";
};
print "Program berlanjut setelah blok try\n";Tertangkap: berat sampel melebihi batas
Pembersihan berjalan dalam kondisi apa pun
Program berlanjut setelah blok tryPesan pada blok try diakhiri \n dengan sengaja, sehingga yang tertangkap adalah pesan yang bersih tanpa tambahan lokasi baris. Blok catch mencetaknya melalui $_, sedangkan blok finally tetap berjalan meskipun tidak ada yang perlu dipulihkan. Setelah ketiga blok selesai, program melanjutkan baris berikutnya, yang menegaskan bahwa error telah ditangani sepenuhnya. Untuk program kecil, eval sudah memadai, sedangkan Try::Tiny memberikan struktur yang lebih jelas ketika penanganannya bertingkat.
Di balik antarmukanya, Try::Tiny membangun penanganan errornya di atas eval, dengan penyimpanan pesan yang dikelola modul itu sendiri sehingga $@ tidak mudah tertimpa oleh kode lain. Blok try dan catch sebenarnya berupa subrutin anonim yang diteruskan sebagai argumen fungsi, sehingga tanda titik koma pada akhir blok terakhir diperlukan untuk mengakhiri pemanggilannya. Karena berbentuk subrutin, pernyataan return di dalamnya mengembalikan nilai dari blok tersebut, bukan dari subrutin yang melingkupinya. Isi $_ pada blok catch juga dikembalikan ke nilai semula setelah bloknya selesai, sehingga variabel itu tidak tertinggal berisi pesan error.
Kesalahan umum#
- Pesan
dieditulis tanpa\npada program yang sudah selesai diuji, sehingga setiap error menyertakan lokasi baris yang kurang berarti bagi pengguna. Perbaikannya adalah menutup pesan dengan\napabila lokasi barisnya tidak dimaksudkan. warndigunakan untuk kegagalan yang seharusnya menghentikan program, sehingga skrip berlanjut dengan data yang tidak lengkap. Perbaikannya adalah membedakan kedua kejadian tersebut, yaituwarnuntuk penyimpangan yang masih dapat dilewati dandieuntuk kegagalan yang membuat pemrosesan tidak berarti lagi.$@diperiksa setelah beberapa pernyataan lain dijalankan, sehingga isinya sudah terganti oleh error yang lebih baru atau sudah dikosongkan. Perbaikannya adalah memeriksa$@segera setelah blokeval, atau beralih ke Try::Tiny yang menyimpan pesannya secara terpisah.
Ringkasan#
diemencetak pesan ke STDERR dan menghentikan program dengan status keluar bukan nol, sedangkanwarnhanya mencetak peringatan dan program berlanjut.- Tanda
\npada akhir pesandiemenentukan apakah lokasi baris ditambahkan ke pesan atau tidak. - Variabel
$!menyimpan pesan error sistem operasi yang terakhir terjadi, dan polaopen or diemenyambungkannya ke pesan skrip. - Blok
evalmenahan error fatal di$@, sedangkan Try::Tiny menyediakan bloktry,catch, danfinallydengan pesan error di$_.
Referensi#
- perlfunc — dokumentasi resmi Perl
— Mendokumentasikan
die,warn,eval, serta variabel khusus$!dan$@yang menjadi dasar penanganan error. - Try::Tiny — CPAN
— Dokumentasi resmi modul Try::Tiny beserta penjelasan blok
try,catch, danfinally.