5 puan yazan GN⁺ 2023-11-25 | 1 yorum | WhatsApp'ta paylaş
  • ShellCheck, yaygın kabuk betiği hatalarını doğrudan web üzerinde denetleyebilen bir araçtır; yapıştırdığınız koddaki sorunları düzenleyicinin altındaki çıktı bölümünde görebilirsiniz
  • Yerel ortamda cabal, apt, dnf, pkg, brew install gibi yöntemlerle kurulabilir; bu da paket yöneticisi tabanlı kullanım sağlar
  • Örnekler, sh shebang için taşınabilirlik uyarıları, anlamsal sorunlar ve tırnak işleme hataları gibi gerçek betiklerde sıkça gözden kaçan noktaları gösterir
  • GPLv3 lisanslı özgür bir yazılımdır ve GitHub, Wiki ile başlıca editörlerde entegre linter olarak da sunulur
  • CodeClimate, Codacy ve CodeFactor üzerinde GitHub depolarının otomatik denetiminde kullanılabilir; ShellCheck’in kendisi ise Haskell ile yazılmıştır

Kabuk betiği analiz özellikleri

  • ShellCheck, kabuk betiklerinde hata bulan bir analiz aracıdır
  • Betiği web sitesine yapıştırdığınızda, düzenleyicinin altındaki çıktı penceresinde denetim sonuçlarını hemen görebilirsiniz
  • Dahil edilen örnekler, birçok farklı türde sorunun tespit edilebildiğini gösterir
    • Sıradan betiklerde gizli kalan çeşitli sorunlar
    • shebang sh olduğunda ortaya çıkan taşınabilirlik uyarıları
    • Daha üst düzey anlamsal sorunlar
    • Çeşitli tırnak işleme sorunları

Kurulum, lisans ve entegrasyon

  • Yerel kurulum cabal, apt, dnf, pkg, brew install ile yapılabilir
  • GPLv3 lisanslı özgür bir yazılımdır
  • Belgeler ShellCheck Wiki üzerinde bulunabilir
  • GitHub üzerinde açık olarak yayımlanmıştır; web sitesi kodu da birlikte açıktır
  • Dağıtımlar veya paket yöneticileri için paketler zaten sunulmaktadır
  • Başlıca editörlerde entegre linter olarak kullanılabilir
  • CodeClimate, Codacy, CodeFactor üzerinde GitHub depolarının otomatik denetiminde kullanılabilir
  • ShellCheck, Haskell ile yazılmıştır

1 yorum

 
GN⁺ 2023-11-25
Hacker News görüşleri
  • Benim kullandığım ipuçları şöyle. shebang içinde neredeyse her zaman -u (nounset) koyup tanımlanmamış değişkenleri hata haline getirmek iyi olur; sık karşılaşılan istisna ise "${arr[@]}" söz dizimiyle boş bir diziyi genişletirken bunun unbound olarak değerlendirilmesi
    -n (noexec) komutların çalıştırılmasını engellediği için yoksul işi bir dry-run gibi kullanılabilir. -e (errexit) de yararlıdır ama pratikte yalnızca başarısız olan “o komutun kendisi” çıkışa yol açar; buna dikkat etmek gerektiğinden ben şahsen bundan kaçınıp komutların arkasına sık sık || fail "..." eklemeyi tercih ediyorum

    • "${arr[@]}" sorunu yalnızca bash 3 ve öncesinde var; bash 4'ten itibaren değişken gerçekten tanımlanmamış olsa bile [@] unbound variable fırlatmıyor
      Yine de macOS hâlâ varsayılan olarak bash v3 kurduğu ve otomatik güncellemediği için bu sorun sürüyor. bash 3'ün son sürümünün 20 yıl önce çıkmış olması gerçekten akıl almaz. Boş dizi genişletmesinde unbound oluşması ${var+alter} genişletmesiyle aşılabilir: echo "${arr+${arr[@]}}"
    • shebang içine Bash/shell seçenekleri koymayın diye yaygın bir tavsiye de var. Çünkü biri betiği ./my_script.sh yerine bash my_script.sh gibi yorumlayıcıyı açıkça belirterek çalıştırırsa seçenekler uygulanmaz
      Çalıştırma bitini ayarlamamak için bunu yapan epey kişi var; bazen de sebep konuyu yeterince anlamamak oluyor. Bu yüzden shebang'den sonraki ilk satıra set -euo pipefail gibi bir set yazılması sık önerilir; ayrıca #!/usr/bin/env bash gibi shebang'in ek argümanları ele almakta zorlandığı durumlarda da işe yarar
    • -uyu shebang'e koymanın özel bir nedeni var mı, merak ediyorum. set -u yerine neden shebang?
      Bash'te "${arr[@]}" düzgün çalışıyor gibi görünüyor. Diğer yorumlarda da belirtildiği gibi güncel Bash'te daha da iyileşmiş ve sorun yalnızca <= 4.3 için geçerli gibi: https://news.ycombinator.com/item?id=38397241
      Örneğin bash -uc 'unset x; echo "=> ${x[@]}"' ve bash -uc 'x=(); echo "=> ${x[@]}"' boş değerle geçiyor, ama bash -uc 'x=(); echo "=> ${x[0]}"' komutu bash: x[0]: unbound variable bırakıyor. Zsh ilk örneği sevmiyor ama ikisi de bash -uc 'unset x; echo "=> ${x[@]:-null}"' gibi varsayılan değer genişletmesini desteklemeli. -e, fonksiyonlarla birleşince çok kafa karıştırıcı hale geliyor; yıllar geçtikçe daha az seviyorum
    • -e sorunu -o pipefail ile iyi yönetilebilir; bu da geçen yıldan beri POSIX'e dahil
    • Betiği erken sonlandıran seçenekler kullanınca, temizlenmesi gereken dosyalar olduğunda genellikle trap de kullanmak gerekiyordu
      trap harika bir betik yazma özelliği, ama bana kalırsa hakkında yeterince konuşulmuyor
  • Yakın zamanda aritmetik genişletme nedeniyle bir shell script'te yetki yükseltme açığı buldum. https://research.nccgroup.com/2020/05/12/shell-arithmetic-ex... adresindekine benzer bir tür
    Örneğin $((1 + ENV_VAR)), $ENV_VAR üzerinde kontrolünüz varsa kod enjeksiyonuna izin verebilir. Ne yazık ki ShellCheck bunu en azından varsayılan ayarlarla yakalayamadı. Ama güvenlik açısından az da olsa önemli bir şey geliştiriyorsanız zaten baştan shell kullanmamalısınız

    • Daha fazla güvenlik istiyorsak ne kullanmalıyız?
  • ShellCheck gerçekten kurtarıcı. Daha önce küçük bir sarmalayıcı olan https://github.com/jamespwilliams/strictbash'i yapmıştım; betiğin shebang'i olarak kullanılabiliyor
    Betik çalışmadan önce ShellCheck'i çalıştırıyor; hata varsa betik hiç çalıştırılmıyor ve bash'in “strict mode” bayraklarının tamamını da ayarlıyor. Referans: http://redsymbol.net/articles/unofficial-bash-strict-mode/

    • Güzel ama yalnızca kendi betiklerinde kullanınca anlamlı gibi. Aksi halde çalıştırman gereken tüm betikleri her seferinde debug edip düzeltmen gerekir
  • Bu konu birkaç kez gündeme geldi: https://news.ycombinator.com/from?site=shellcheck.net
    Son büyük tartışma 2021'deydi; 301 puan ve 54 yorum vardı: https://news.ycombinator.com/item?id=27030504

  • Kısa süre önce derleme ve dağıtım betiklerini, ayrıca tek bir üretim sunucusu için birkaç bash betiğini Haskell’in Turtle’ına taşıdım
    Tekrarı ciddi ölçüde azaltabildiğim için iyiydi; ortaya çıkan kod da çok daha kısa oldu. https://hackage.haskell.org/package/turtle

    • Yakın zamanda Turtle’ı denedim ama sonunda bırakıp typed-process’i seçtim
      Bildiğim kadarıyla Turtle programlarında yalnızca tek bir geçerli dizin var; bu da belirli bir dizinde çalışması gereken eşzamanlı işleri yürütürken zorluk çıkarıyor. Kilit, kuyruk ve worker yaklaşımıyla bir kısmını çözdüm ama Turtle’ın geçerli dizini silinip işler hata vermeye başlayınca başa çıkmak zorlaştı
      Buna karşılık typed-process ayrı süreçler başlatıyor ve cd yapmaya gerek kalmadan çalışma dizini içinde çalıştırılabiliyor; bu yüzden büyük ve karmaşık iş akışlarına iyi uyuyor. OverloadedStrings desteği de iyi, bu nedenle normalde bash’e yazacağınız şeyleri kopyalayıp yapıştırınca aynen çalışıyor
      Ham string’leri kaynak kodda daha okunur kılmak için interpolate paketi ve QuasiQuotes da kullanıyorum; ancak hlint ile uyumlu olmadığı için string işleme için başka bir paket bakmayı düşünüyorum
  • Açıkça öz tanıtım olacak ama commit’ten önce ya da en azından merge’den önce tüm uyarıları düzeltme ilkesiyle ShellCheck’i ve çeşitli linter’ları pre-commit yapılandırmama koyuyorum
    Ancak projelerimin çoğundaki shell kodu .gitlab-ci.yml dosyasının içinde olduğundan kontrol etmesi zor. Bu yüzden bunu otomatik yapan bir wrapper yazdım: https://pypi.org/project/glscpc/
    ShellCheck projesini ve biraz sihri kullanarak ShellCheck uyarılarını neredeyse doğru satır numaralarıyla gösteriyor

    • Bunu daha genel şekilde yapan bir proje olsa güzel olurdu
      GitLab CI kullanmıyorum ama Dockerfile, GitHub Actions, Justfile gibi, özünde shell script’i satır içine gömen epey çok dosya biçimi kullanıyorum
      Genelde, sırf ShellCheck için bile olsa birkaç komuttan daha karmaşık şeyleri ayrı bir shell script’e çıkarıp Dockerfile’daki satır içi betikten çağırıyorum. Bu kalıp, CI’ın GitHub Actions’a fazla sıkı bağlanmamasına da yardımcı oluyor
    • Çak bir beşlik. Ben de çok yakın zamanda benzer bir şey yaptım: https://gitlab.com/engmark/shellcheck-gitlab-ci-scripts-hook
      Kullanım örneği .gitlab-ci.yml için bir pre-commit hook’u; yapılandırma örneği de burada: https://gitlab.com/engmark/root/-/blob/9f7d9b93c2297d0b170e5...
    • Gerçekten harika. Eskiden bu sorunu başka bir açıdan çözmeyi denemiştim. “Normal” bir shell script’i alıp derleme zamanında ilgili işin script kısmı olarak render eden bir ön işleme eklemek istiyordum
      Avantajı, her şeyin hâlâ gitlab-ci işi içinde kendi kendine yeterli kalmasıydı. Ama GitLab CI runner ortamında shell’in her türlü acayipliğiyle uğraşmak çok eziyetli olduğu için bıraktım; şimdi tüm işleri Python script’lerine taşıyorum
  • bash language server da var: https://github.com/bash-lsp/bash-language-server/

  • Güzel. İlk kez çalıştırdığım üretim /bin/sh betiğinde hemen birkaç şey öğrendim; oysa bu tür betiklerle 80’lerden beri uğraşıyorum

  • Bash ile yazmak için fazla uzayıp aslında öyle yapılmaması gereken bir durumdaysanız https://github.com/bach-sh/bach de tavsiye ederim

  • ShellCheck harika ama source/import işleme gerçekten acı verici. Bu ShellCheck’in suçu değil; sh bir kâbus olduğu için böyle

    • Şöyle yapılabilir: # shellcheck source=./deployment/deployment-example.env yazıp ardından . "${1}" kullanmak
      Ama çok sayıda alt shell script’i ve source edilecek dosya çoğaldığında bunun neden acı verici olduğunu anlıyorsunuz