İçeriğe geç
academia.sh

Ders 08 / 24

Özel Değişkenler ve Argümanlar

Konumsal parametreler, argüman listesinin iki gösterimi, listeyi tüketen döngü ve seçenek ayrıştırma.

İçindekiler

Kütük adı artık bir değişkenden geliyor, ama betiği çağıran için doğal yol bu değildir. KUTUK=erisim.log ./rapor.sh yazmak yerine ./rapor.sh erisim.log yazmak beklenir. Komut satırından gelen değerler, kabukta ayrı bir mekanizmayla — konumsal parametreler (positional parameters) ile — karşılanır.

Programlama Temelleri kursunda fonksiyon parametreleri adlarıyla tanımlanıyordu. Kabukta adlandırma yoktur: parametreler numaralanır ve numaralarıyla okunur. Bu ders o numaraları, listeyi bir bütün olarak ele almanın iki yolunu ve seçenek ayrıştırmayı kurar.

Konumsal Parametreler

$1, $2, … betikçe alınan argümanları sırayla verir. $0 betiğin çağrıldığı adı, $# argüman sayısını tutar.

#!/usr/bin/env bash
echo "betik adi     : $0"
echo "arguman sayisi: $#"
echo "ilk           : $1"
echo "ikinci        : $2"
echo "onuncu        : ${10}"
./arg.sh a b c d e f g h i j
betik adi     : ./arg.sh
arguman sayisi: 10
ilk           : a
ikinci        : b
onuncu        : j

Dokuzdan büyük numaralar süslü ayraç ister. $10 yazılsaydı kabuk bunu $1 ve 0 diye okur, ilk argümanın ardına sıfır ekleyerek a0 üretirdi. Ayraç kullanımı bu yüzden zorunludur.

$0, betiğin çalıştırılırken yazıldığı addır; dosyanın gerçek adı olmak zorunda değildir ve saltık yol içermeyebilir. Kullanım iletilerinde $0 yazmak yaygındır ama betiğin adını sabit yazmak daha okunaklı bir çıktı verir.

Argüman Listesi: "$@" ve "$*"

Tüm argümanları birlikte aktarmak için iki gösterim vardır ve aralarındaki fark yalnızca tırnak içinde ortaya çıkar.

  • "$@", her argümanı ayrı bir kelime olarak genişletir.
  • "$*", tüm argümanları IFS’in ilk karakteriyle birleştirip tek kelime üretir.
  • Tırnaksız $@ ve $*, ikisi de kelime bölmeye uğrar ve ayrım kaybolur.
#!/usr/bin/env bash
say() { printf 'sayi=%d\n' "$#"; printf '  <%s>\n' "$@"; }
echo '--- "$@" ---'; say "$@"
echo '--- "$*" ---'; say "$*"
echo '--- $@   ---'; say $@
./liste.sh "erisim kutugu.log" 404
--- "$@" ---
sayi=2
  <erisim kutugu.log>
  <404>
--- "$*" ---
sayi=1
  <erisim kutugu.log 404>
--- $@   ---
sayi=3
  <erisim>
  <kutugu.log>
  <404>

Üç sonuç üç farklı anlam taşır. İlki, çağıranın verdiği sınırları koruyarak listeyi aktarır. İkincisi listeyi tek metne çevirir — ileti yazdırmak dışında bir işe yaramaz. Üçüncüsü, boşluk içeren argümanı ikiye böler ve çağıranın niyetini bozar.

Kural: argümanları başka bir komuta aktarırken her zaman "$@" kullanın. Bu, önceki dersteki tırnaklama kuralının konumsal parametrelere uzanmasıdır.

Listeyi Tüketmek

shift komutu konumsal parametreleri bir sola kaydırır: $2 yeni $1 olur, $# bir azalır. Argümanları sırayla işleyen döngülerin temelidir.

#!/usr/bin/env bash
while [ "$#" -gt 0 ]; do
  echo "islenen: $1 (kalan: $#)"
  shift
done
./shift.sh bir iki uc
islenen: bir (kalan: 3)
islenen: iki (kalan: 2)
islenen: uc (kalan: 1)

shift N biçimi bir defada N parametre atar. Kalan parametre sayısından fazlasını atmaya çalışmak sıfırdan farklı kod döndürür ve listeyi değiştirmez.

Betiğe İlişkin Özel Değişkenler

Kabuk, çalışan betik hakkında birkaç değeri özel adlarda tutar.

Ad İçerik
$? son komutun çıkış kodu
$$ çalışan kabuğun süreç numarası
$! arka planda başlatılan son sürecin numarası
$- etkin kabuk seçenekleri
$0 betiğin çağrıldığı ad
$# konumsal parametre sayısı

$$ geçici dosya adı üretmekte sık kullanılır; ancak süreç numaraları yeniden kullanılabildiği için tek başına güvenli değildir. Tuzaklar ve Temizlik dersinde mktemp ile doğru yol gösterilecek.

$! arka plan süreçlerini beklemek ve durdurmak için gerekir; Çıkış Kodları dersinde sinyal kodları bu değişkenle üretilmişti.

Seçenek Ayrıştırma

Argümanları elle ayrıştırmak — birinci argüman seçenekse, ikinci onun değeriyse … — kısa sürede okunmaz hâle gelir. getopts yerleşik komutu kısa seçenekleri sözleşmeye uygun biçimde ayrıştırır.

getopts iki argüman alır: tanınan seçeneklerin dizisi ve bulunan seçeneğin yazılacağı değişken adı. Dizideki bir harfin ardından : gelirse o seçenek değer bekler; değer OPTARG değişkenine konur. Dizinin başındaki :, hata iletilerini getopts’un değil betiğin üreteceğini bildirir.

#!/usr/bin/env bash
adet=5
ayrinti=0
while getopts ':n:va' secenek; do
  case "$secenek" in
    n)  adet="$OPTARG" ;;
    v)  ayrinti=1 ;;
    a)  echo "kullanim: sec.sh [-n SAYI] [-v] KUTUK"; exit 0 ;;
    :)  echo "hata: -$OPTARG deger bekliyor" >&2; exit 2 ;;
    \?) echo "hata: bilinmeyen secenek -$OPTARG" >&2; exit 2 ;;
  esac
done
shift $((OPTIND - 1))
echo "adet=$adet ayrinti=$ayrinti kalan=$# ilk=[${1-yok}]"

Dört çağrı, dört davranışı gösterir:

./sec.sh -n 3 -v erisim.log
./sec.sh erisim.log
./sec.sh -n
./sec.sh -z
adet=3 ayrinti=1 kalan=1 ilk=[erisim.log]
adet=5 ayrinti=0 kalan=1 ilk=[erisim.log]
hata: -n deger bekliyor
hata: bilinmeyen secenek -z

Son iki çağrı 2 koduyla sonlandı. Kullanım hatasını başka bir hata sınıfından ayrı bir kodla bildirmek, betiği zincirde kullanılabilir kılar.

OPTIND, getopts’un işlediği son argümanın sırasını tutar. Döngüden sonraki shift $((OPTIND - 1)) satırı seçenekleri listeden atar; geriye yalnızca seçenek olmayan argümanlar kalır. Bu satır unutulursa $1 hâlâ ilk seçeneği gösterir.

getopts yalnızca tek harfli seçenekleri tanır; --ayrinti gibi uzun seçenekler için destek yoktur. Uzun seçenek gerekiyorsa whilecase ile elle ayrıştırma yazılır. Ayrıca case dallarındaki \? yazımındaki ters bölü gereklidir: ? case kalıplarında tek karakter eşleyen özel bir simgedir.

Argüman Sonu İşareti

Tire ile başlayan bir değer, seçenek sanılabilir. Örneğin -eski.log adlı bir dosya, çoğu komut tarafından seçenek olarak yorumlanır. Sözleşme, argüman listesinde -- görüldüğünde ondan sonrasının seçenek olmadığını kabul etmektir:

grep -- '-404-' erisim.log

Kendi betiklerinizde de getopts bu işareti tanır ve orada durur. Değişkenden gelen dosya adlarını başka bir komuta aktarırken -- yazmak, kullanıcı girdisiyle seçenek karışmasını önler.

Betiğe Uygulama

rapor.sh artık bir arayüz kazanır: kaç satır listeleneceği -n ile, kütük adı ilk konumsal parametreyle verilir.

#!/usr/bin/env bash
# rapor.sh — erisim kutugunden ozet rapor uretir.
# Kullanim: rapor.sh [-n SAYI] [KUTUK]

adet=5

while getopts ':n:' secenek; do
  case "$secenek" in
    n)  adet="$OPTARG" ;;
    :)  echo "hata: -$OPTARG deger bekliyor" >&2; exit 2 ;;
    \?) echo "hata: bilinmeyen secenek -$OPTARG" >&2; exit 2 ;;
  esac
done
shift $((OPTIND - 1))

kutuk="${1:-erisim.log}"

echo "== Kutuk: $kutuk (ilk $adet yol) =="
cut -d' ' -f7 "$kutuk" | sort | uniq -c | sort -rn | head -"$adet"
./rapor.sh -n 3
== Kutuk: erisim.log (ilk 3 yol) ==
   6 /api/veri
   4 /yok.html
   4 /index.html

Betik hâlâ eksik: verilen kütük yoksa cut hata verir ve rapor başlığı boşuna basılmış olur. Bu eksik sonraki dersin konusudur.

Özet

  • Konumsal parametreler $1’den başlar; dokuzdan büyük numaralar süslü ayraç ister. $# sayıyı, $0 çağrılan adı verir.
  • "$@" argümanları ayrı kelimeler olarak, "$*" tek kelime olarak genişletir; tırnaksız yazımda ikisi de kelime bölmeye uğrar. Aktarımda "$@" kullanılır.
  • shift listeyi bir sola kaydırır ve argümanları sırayla tüketen döngüyü mümkün kılar.
  • getopts kısa seçenekleri ayrıştırır; OPTARG değeri, OPTIND işlenen sırayı tutar ve döngüden sonra shift $((OPTIND - 1)) gerekir.
  • -- işareti, ondan sonrasının seçenek olmadığını bildirir ve tire ile başlayan değerleri korur.

Sonraki Adım

Betik, kendisine verilen kütüğün var olduğunu ve okunabildiğini varsayıyor. Bu varsayımı sınamak için bir koşul gerekir; kabukta koşul, bir komutun çıkış kodudur. Sonraki ders dosya, dizgi ve sayı testlerini, [ ] ile [[ ]] arasındaki farkı ve bu farkın neden sözdizim tercihi değil doğruluk meselesi olduğunu ele alır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat