shellcheck

Analisis statis dengan shellcheck sebagai linter sekaligus guru gaya penulisan skrip.

Daftar isi

Menjalankan skrip bukan satu-satunya cara menemukan cacatnya, karena sebagian kelemahan dapat dikenali hanya dari membaca kode itu sendiri. shellcheck adalah linter (pemeriksa otomatis yang menandai kode bermasalah) untuk skrip shell yang bekerja dengan analisis statis, yaitu memeriksa kode tanpa mengeksekusinya. Topik ini membahas cara menjalankan shellcheck, membaca temuannya, memperbaiki skrip yang dilaporkan, dan memanfaatkan linting (kegiatan memeriksa kode dengan linter) sebagai guru gaya penulisan. Contohnya memakai sepasang skrip kecil, satu sengaja mengandung kelemahan dan satu versi perbaikannya, sehingga setiap temuan dapat ditelusuri sampai baris penyebabnya.

Menjalankan shellcheck#

Perintah paling dasar berupa shellcheck nama-skrip.sh, sedangkan --version menampilkan identitas alat yang terpasang. Jalankan perintah berikut untuk memastikan versi yang dipakai.

bash
# periksa versi shellcheck yang terpasang
shellcheck --version
text Keluaran
ShellCheck - shell script analysis tool
version: 0.11.0
license: GNU General Public License, version 3
website: https://www.shellcheck.net

Versi 0.11.0 pada lingkungan contoh ini memakai lisensi terbuka dan situs dokumentasinya sama dengan yang dirujuk pesan temuan. Keluaran --version juga memastikan bahwa perintah shellcheck benar-benar ditemukan pada PATH (daftar direktori tempat perintah dicari) sebelum contoh-contoh berikutnya dipraktikkan.

shellcheck menerima nama berkas skrip sebagai argumen, membaca shebang-nya untuk menentukan dialek shell, lalu menilai seluruh isinya tanpa menjalankan satu perintah pun. Pemeriksaan tidak menuntut skrip yang sudah bisa dieksekusi, sehingga dapat dilakukan sebelum skrip pertama kali dijalankan. Hasil pemeriksaan disertai kode keluar yang berguna, yaitu 0 bila tidak ada temuan dan bukan 0 bila ada, sehingga alat otomatis dapat mengandalkannya tanpa membaca teks laporannya. Bagian berikut membandingkan dua skrip yang saling berkaitan untuk menunjukkan perbedaan antara kode yang lolos dijalankan dan kode yang lolos diperiksa.

Membaca temuan pada skrip yang rusak#

Skrip berikut sengaja ditulis dengan kelemahan yang lazim pada skrip shell, yaitu variabel yang didefinisikan tetapi tidak pernah dipakai dan perluasan variabel tanpa tanda kutip. Jalankan terlebih dahulu untuk melihat perilakunya dari sisi pengguna.

bash skrip-rusak.sh
#!/usr/bin/env bash
# Skrip sengaja rusak untuk demo shellcheck.

berkas="catatan.txt"
jumlah=3
nama="Dewi"

echo "memeriksa $berkas"
cat $berkas

if [ $jumlah -eq 3 ]
then
  echo "jumlah bernilai tiga"
fi
text Keluaran
memeriksa catatan.txt
cat: catatan.txt: No such file or directory
jumlah bernilai tiga

Skrip ini berakhir dengan kode keluar 0 meskipun cat gagal, karena strict mode tidak diaktifkan dan pesan gagalnya hanya melintas di layar. Periksa kode yang sama dengan shellcheck untuk melihat apa yang ditemukan dari pembacaan statis.

bash
# periksa skrip yang rusak
shellcheck skrip-rusak.sh
echo "kode keluar: $?"
text Keluaran

In skrip-rusak.sh line 6:
nama="Dewi"
^--^ SC2034 (warning): nama appears unused. Verify use (or export if used externally).

For more information:
  https://www.shellcheck.net/wiki/SC2034 -- nama appears unused. Verify use (...
kode keluar: 1

Terdapat satu temuan, yaitu SC2034 dengan tingkat keparahan severity (label yang menyatakan seberapa serius suatu temuan) berupa warning. Pesannya memuat semua yang dibutuhkan untuk memperbaikinya, yaitu nama berkas, nomor baris, potongan kode bermasalah, tanda panah yang menunjuk posisi persisnya, penjelasan singkat, dan tautan wiki yang membahas kodenya lebih jauh. Variabel nama memang didefinisikan tetapi tidak pernah dipakai di bagian mana pun, sehingga laporan itu benar. Kode keluar berubah menjadi 1, dan itulah sinyal yang dibaca alat otomatis.

Memperbaiki dan memverifikasi ulang#

Versi perbaikannya menyentuh tiga hal sekaligus. Variabel nama yang tadinya menganggur kini ikut dicetak sehingga temuan SC2034 hilang. Setiap perluasan variabel diberi tanda kutip ganda, kebiasaan yang mencegah pemecahan kata (word splitting) dan pengembangan pola (globbing) apabila nilai variabel mengandung spasi atau karakter khusus. Struktur if dirapatkan menjadi satu baris agar gayanya konsisten dengan skrip lain pada jalur ini.

bash skrip-baik.sh
#!/usr/bin/env bash
# Versi perbaikan dari skrip-rusak.sh.

berkas="catatan.txt"
jumlah=3
nama="Dewi"

echo "memeriksa $berkas untuk $nama"
cat "$berkas"

if [ "$jumlah" -eq 3 ]; then
  echo "jumlah bernilai tiga"
fi
text Keluaran
memeriksa catatan.txt untuk Dewi
cat: catatan.txt: No such file or directory
jumlah bernilai tiga

Pesan cat: catatan.txt: No such file or directory tetap muncul karena demo ini memang tidak menyediakan berkas tersebut, dan bagian itu tidak berubah antara kedua versi skrip. Yang dinilai di sini adalah cara skrip memperlakukan variabelnya, bukan keberadaan berkasnya. Verifikasi ulang versi perbaikannya dengan shellcheck.

bash
# verifikasi ulang versi perbaikannya
shellcheck skrip-baik.sh && echo "temuan: tidak ada"
text Keluaran
temuan: tidak ada

Perintah shellcheck skrip-baik.sh tidak mencetak apa pun dan kembali dengan kode 0, sehingga bagian && berjalan dan mencetak temuan: tidak ada. Pola shellcheck skrip.sh && ... semacam ini mudah dipasang pada skrip pemeriksa karena kodenya kembali 0 hanya bila skrip yang diperiksa bersih.

Linter sebagai guru gaya#

Nilai jangka panjang shellcheck bukan sekadar daftar kesalahan, melainkan kebiasaan yang diajarkannya. Setiap nomor temuan memiliki halaman wiki yang menjelaskan aturan di baliknya, alasan keamanannya, dan contoh perbaikannya, sehingga mempelajari satu temuan biasanya menutup satu kelas kelemahan. Temuan juga bertingkat menurut severity, yaitu error untuk kode yang tidak dapat diurai, warning untuk kelemahan nyata, lalu info dan style untuk kerapian. Opsi --severity membatasi laporan sampai tingkat tertentu saja, misalnya hanya warning ke atas untuk pemeriksaan cepat.

Kebiasaan yang dianjurkan adalah menjalankan shellcheck setiap kali skrip disunting, bukan sekali di akhir saja, karena temuan yang baru muncul lebih mudah ditelusuri saat perubahannya masih segar. Sebagian besar aturannya sejalan dengan panduan gaya resmi, sehingga menuruti temuan sekaligus merapikan gaya penulisan. Dengan kebiasaan itu, seluruh skrip contoh pada modul ini lolos pemeriksaan tanpa temuan.

Kesalahan umum#

Kesalahan berikut sering muncul ketika linter baru masuk ke kebiasaan kerja sehari-hari, dan ketiganya menyangkut cara alat ini ditempatkan dalam pekerjaan, bukan isi laporannya.

  • Memperlakukan shellcheck sebagai pengganti pengujian. Analisis statis menemukan pola yang berisiko, tetapi tidak menjalankan skrip, sehingga jalur yang sah menurut linter tetap perlu diuji dengan masukan nyata. Uji jalur kritisnya dengan masukan yang benar maupun yang salah sebelum skrip dipakai pada otomasi.
  • Mematikan temuan tanpa alasan. Menambahkan # shellcheck disable= pada banyak baris menghilangkan nilai linter itu sendiri. Perbaiki penyebabnya bila memungkinkan, sisakan pengecualian hanya untuk kasus yang memang disengaja, dan tindakan pengecualian yang menumpuk biasanya menandakan aturan penulisan perlu dibicarakan ulang.
  • Mengabaikan pesan karena berbahasa Inggris. Struktur pesannya selalu sama, yaitu berkas, baris, kode, dan tautan wiki, sehingga beberapa pesan pertama sudah cukup untuk mengenali polanya. Nomor kodenya juga dapat langsung ditelusuri ke wiki tanpa menerjemahkan seluruh pesan.

Ringkasan#

  • shellcheck memeriksa skrip secara statis tanpa mengeksekusinya dan melaporkan temuan dengan kode, baris, serta tautan wiki.
  • Temuan pada skrip-rusak.sh berupa SC2034 untuk variabel yang didefinisikan tetapi tidak dipakai.
  • Versi perbaikannya memakai variabel yang ada dan mengutip setiap perluasannya, lalu lulus pemeriksaan dengan kode keluar 0.
  • Kode keluar shellcheck, yaitu 0 bila bersih dan bukan 0 bila ada temuan, membuat pemeriksaannya mudah dipasang pada alat otomatis.

Referensi#