5 poin oleh GN⁺ 2023-11-25 | 1 komentar | Bagikan ke WhatsApp
  • ShellCheck adalah alat untuk memeriksa kesalahan umum pada skrip shell langsung di web, dan Anda dapat melihat masalah pada kode yang ditempel melalui output di bawah editor
  • Di lingkungan lokal, alat ini dapat dipasang dengan cabal, apt, dnf, pkg, brew install, dan lain-lain sehingga dapat digunakan berbasis package manager
  • Contohnya menunjukkan hal-hal yang sering terlewat dalam skrip nyata, seperti peringatan portabilitas pada shebang sh, masalah semantik, dan kesalahan penanganan tanda kutip
  • Ini adalah perangkat lunak bebas dengan lisensi GPLv3, dan juga tersedia di GitHub, Wiki, serta sebagai linter terintegrasi untuk editor-editor utama
  • Dapat digunakan untuk pemeriksaan otomatis repositori GitHub di CodeClimate, Codacy, dan CodeFactor, dan ShellCheck sendiri ditulis dengan Haskell

Fitur analisis skrip shell

  • ShellCheck adalah alat analisis untuk menemukan bug pada skrip shell
  • Jika Anda menempelkan skrip ke situs web, Anda dapat langsung melihat hasil pemeriksaan pada jendela output di bawah editor
  • Contoh-contoh yang disertakan menunjukkan bahwa alat ini dapat mendeteksi berbagai jenis masalah
    • Beragam masalah yang tersembunyi dalam skrip umum
    • Peringatan portabilitas yang muncul ketika shebang adalah sh
    • Masalah semantik tingkat lebih tinggi
    • Berbagai masalah penanganan tanda kutip

Instalasi, lisensi, integrasi

  • Instalasi lokal dapat dilakukan dengan cabal, apt, dnf, pkg, brew install
  • Ini adalah perangkat lunak bebas dengan lisensi GPLv3
  • Dokumentasi dapat dilihat di ShellCheck Wiki
  • Tersedia secara publik di GitHub, dan kode situs web juga dibuka untuk umum
  • Paket untuk distribusi atau package manager sudah tersedia
  • Dapat digunakan sebagai linter terintegrasi di editor-editor utama
  • Dapat dimanfaatkan untuk pemeriksaan otomatis repositori GitHub di CodeClimate, Codacy, dan CodeFactor
  • ShellCheck ditulis dengan Haskell

1 komentar

 
GN⁺ 2023-11-25
Komentar Hacker News
  • Tips yang saya pakai seperti ini. Di shebang, hampir selalu sebaiknya memasukkan -u (nounset) agar variabel yang belum dideklarasikan menjadi error; pengecualian yang sering ditemui adalah ketika ekspansi array kosong dengan sintaks "${arr[@]}" dianggap unbound
    -n (noexec) mencegah eksekusi perintah, jadi bisa dipakai seperti dry-run versi orang miskin. -e (errexit) juga berguna, tetapi perlu berhati-hati karena pada praktiknya hanya perintah yang gagal “itu sendiri” yang menyebabkan terminasi; secara pribadi saya cenderung menghindarinya dan lebih sering menambahkan || fail "..." setelah perintah

    • Masalah "${arr[@]}" hanya ada di bash 3 ke bawah, dan mulai bash 4, [@] tidak melempar unbound variable bahkan ketika variabel benar-benar belum didefinisikan
      Meski begitu, macOS masih memasang bash v3 secara default dan juga tidak memperbaruinya otomatis, jadi ini tetap menjadi masalah. Fakta bahwa rilis terakhir bash 3 sudah 20 tahun lalu benar-benar gila. Unbound saat ekspansi array kosong bisa diakali dengan ekspansi ${var+alter}: echo "${arr+${arr[@]}}"
    • Ada juga saran umum untuk tidak memasukkan opsi Bash/shell ke shebang. Alasannya, jika seseorang menjalankan skrip dengan menyebutkan interpreter secara eksplisit seperti bash my_script.sh, bukan ./my_script.sh, opsi itu tidak akan diterapkan
      Cukup banyak orang melakukan ini agar tidak perlu mengatur bit eksekusi, dan kadang juga karena kurang paham. Jadi saran yang umum adalah memakai set seperti set -euo pipefail pada baris pertama setelah shebang, dan ini juga membantu ketika shebang sulit menangani argumen tambahan seperti #!/usr/bin/env bash
    • Saya penasaran apakah ada alasan khusus memasukkan -u ke shebang. Mengapa shebang, bukan set -u?
      Di Bash, "${arr[@]}" tampaknya bekerja dengan baik. Seperti disebutkan komentar lain, di Bash terbaru sudah lebih baik, dan sepertinya hanya bermasalah pada <= 4.3: https://news.ycombinator.com/item?id=38397241
      Misalnya bash -uc 'unset x; echo "=> ${x[@]}"' dan bash -uc 'x=(); echo "=> ${x[@]}"' lolos dengan nilai kosong, tetapi bash -uc 'x=(); echo "=> ${x[0]}"' menghasilkan bash: x[0]: unbound variable. Zsh tidak menyukai contoh pertama, tetapi keduanya semestinya mendukung ekspansi nilai default seperti bash -uc 'unset x; echo "=> ${x[@]:-null}"'. -e menjadi sangat membingungkan ketika terkait fungsi, jadi makin lama saya makin kurang menyukainya
    • Masalah -e bisa ditangani dengan baik memakai -o pipefail, dan ini sudah masuk POSIX sejak tahun lalu
    • Jika memakai opsi yang membuat skrip berhenti lebih awal, ketika ada file yang perlu dibersihkan biasanya harus memakai trap juga
      trap adalah fitur scripting yang luar biasa, tetapi menurut saya tidak cukup sering dibahas
  • Baru-baru ini saya menemukan kerentanan eskalasi hak akses di shell script akibat ekspansi aritmetika. Tipenya mirip dengan yang dijelaskan di https://research.nccgroup.com/2020/05/12/shell-arithmetic-ex...
    Misalnya $((1 + ENV_VAR)) memungkinkan injeksi kode jika $ENV_VAR bisa dikendalikan. Sayangnya, ShellCheck tidak menangkap ini, setidaknya dengan konfigurasi default. Namun jika Anda mengimplementasikan sesuatu yang sedikit saja penting untuk keamanan, sejak awal jangan memakai shell

    • Kalau ingin lebih aman, sebaiknya pakai apa?
  • ShellCheck benar-benar penyelamat. Dulu saya membuat wrapper kecil https://github.com/jamespwilliams/strictbash yang bisa dipakai sebagai shebang skrip
    Sebelum skrip dijalankan, ia menjalankan ShellCheck, dan jika ada kegagalan skrip sama sekali tidak dijalankan; semua flag “strict mode” bash juga diaktifkan. Referensi: http://redsymbol.net/articles/unofficial-bash-strict-mode/

    • Bagus sih, tetapi sepertinya efektif hanya untuk skrip sendiri. Kalau tidak, Anda harus men-debug dan memperbaiki semua skrip yang perlu dijalankan setiap kali
  • Topik ini sudah beberapa kali muncul: https://news.ycombinator.com/from?site=shellcheck.net
    Diskusi besar terakhir terjadi pada 2021, dengan 301 poin dan 54 komentar: https://news.ycombinator.com/item?id=27030504

  • Belum lama ini saya mengganti skrip build dan deployment, serta beberapa skrip bash untuk satu server produksi, dengan Turtle dari Haskell
    Rasanya bagus karena bisa sangat mengurangi duplikasi, dan kode hasilnya juga jadi jauh lebih pendek. https://hackage.haskell.org/package/turtle

    • Baru-baru ini saya mencoba Turtle, tetapi akhirnya meninggalkannya dan memilih typed-process
      Sejauh yang saya tahu, program Turtle hanya punya satu direktori saat ini, jadi sulit saat menjalankan pekerjaan bersamaan yang harus dieksekusi di direktori tertentu. Saya sempat mengatasinya sebagian dengan lock, queue, dan pola worker, tetapi ketika direktori saat ini milik Turtle dihapus lalu mulai gagal, itu menjadi sulit ditangani
      Sebaliknya, typed-process menjalankan proses terpisah, dan bisa dieksekusi di dalam direktori kerja tanpa perlu cd, jadi cocok untuk workflow yang besar dan kompleks. Dukungan OverloadedStrings-nya juga bagus, sehingga biasanya apa yang akan diketik di bash bisa disalin-tempel dan langsung berjalan
      Saya juga memakai paket interpolate dan QuasiQuotes agar raw string terlihat lebih baik di source code, tetapi karena tidak kompatibel dengan hlint, saya berencana mencari paket lain untuk penanganan string
  • Ini promosi diri yang terang-terangan, tetapi saya memasukkan ShellCheck dan beberapa linter ke konfigurasi pre-commit dengan prinsip memperbaiki semua peringatan sebelum commit, atau setidaknya sebelum merge
    Namun sebagian besar shell di proyek saya ada di dalam file .gitlab-ci.yml, jadi sulit diperiksa. Karena itu saya membuat wrapper yang menanganinya otomatis: https://pypi.org/project/glscpc/
    Dengan proyek ShellCheck dan sedikit sihir, wrapper ini menampilkan temuan ShellCheck dengan nomor baris yang hampir akurat

    • Akan menyenangkan kalau ada proyek yang melakukan ini secara lebih umum
      Saya tidak memakai GitLab CI, tetapi cukup sering memakai format file yang pada dasarnya menyisipkan shell script secara inline, seperti Dockerfile, GitHub Actions, dan Justfile
      Biasanya, demi ShellCheck saja, apa pun yang lebih kompleks daripada beberapa perintah saya pisahkan ke shell script tersendiri, lalu dipanggil dari skrip inline di Dockerfile. Pola ini juga membantu agar CI tidak terlalu terikat pada GitHub Actions
    • High five. Saya juga baru-baru ini membuat sesuatu yang mirip: https://gitlab.com/engmark/shellcheck-gitlab-ci-scripts-hook
      Contoh penggunaannya adalah hook pre-commit untuk .gitlab-ci.yml, dan contoh konfigurasinya ada di sini: https://gitlab.com/engmark/root/-/blob/9f7d9b93c2297d0b170e5...
    • Benar-benar keren. Dulu saya pernah mencoba menyelesaikan masalah ini dari sudut pandang lain. Saya ingin menambahkan preprocessing yang menerima shell script “biasa” lalu merendernya pada waktu build menjadi bagian script dari job tersebut
      Kelebihannya, semuanya tetap self-contained di dalam job gitlab-ci. Namun menangani segala macam keanehan shell di lingkungan GitLab CI runner terlalu menyakitkan, jadi saya berhenti, dan sekarang saya sedang memindahkan semua job ke Python script
  • Ada juga bash language server: https://github.com/bash-lsp/bash-language-server/

  • Bagus. Dari skrip produksi /bin/sh pertama yang saya jalankan, saya langsung belajar beberapa hal, padahal saya sudah mengutak-atik skrip seperti ini sejak era 80-an

  • Jika sudah terlalu panjang untuk ditulis dengan Bash sehingga sebenarnya seharusnya tidak memakai Bash, saya juga merekomendasikan https://github.com/bach-sh/bach

  • ShellCheck memang hebat, tetapi penanganan source/import benar-benar menyakitkan. Ini bukan salah ShellCheck; masalahnya sh memang mimpi buruk

    • Bisa dilakukan seperti ini: tulis # shellcheck source=./deployment/deployment-example.env, lalu gunakan . "${1}"
      Namun kalau ada banyak shell script turunan dan banyak file yang harus di-source, akan terasa kenapa ini menyakitkan