İçeriğe geç
academia.sh

Ders 17 / 24

Alan Tabanlı İşleme

Kayıt ve alan modeli, kalıp–eylem yapısı, yerleşik değişkenler, ilişkisel dizilerle birikim ve tek geçişte rapor üretimi.

İçindekiler

Akış editörü satırı bir metin olarak görür; alanları ancak kalıpla taklit edebilir. Kütükteki dokuzuncu alanı almak için satırın tamamını betimleyen bir kalıp yazmak gerekmişti ve o kalıp, biçim küçük bir değişikliğe uğradığında bozulur.

Sütunlu veri için ayrı bir araç sınıfı vardır. Alan tabanlı işleme aracı, girdiyi kayıtlara, kayıtları alanlara böler ve kalıp–eylem çiftlerinden oluşan bir program yürütür. Bu ders o modeli kurar ve önceki konuda ilişkisel dizilerle yazılan sayımları tek satıra indirir.

Kayıt ve Alan Modeli

Girdi öntanımlı olarak satırlara — kayıtlara — bölünür. Her kayıt, boşluk kümelerine göre alanlara ayrılır. $1 birinci alanı, $0 kaydın tamamını, $NF son alanı verir.

head -2 erisim.log | awk '{ print NR, NF, $1, $9, $NF }'
1 10 10.0.0.12 200 5120
2 10 10.0.0.31 200 2048

Yerleşik değişkenler modelin çevresini tamamlar:

Değişken İçerik
NR okunan kayıt sayısı (dosyalar boyunca birikir)
FNR o dosyadaki kayıt sırası
NF o kayıttaki alan sayısı
FS alan ayırıcısı (öntanımlı: boşluk kümeleri)
OFS çıktı alan ayırıcısı (öntanımlı: tek boşluk)
FILENAME okunmakta olan dosyanın adı

Öntanımlı ayırıcının “boşluk kümeleri” olması önemlidir: art arda gelen boşluklar tek ayırıcı sayılır ve satır başındaki boşluklar yok sayılır. Bu, cut komutundan ayrıldığı noktadır — cut her ayırıcıyı ayrı sayar ve hizalanmış metinlerde boş alanlar üretir.

Kalıp ve Eylem

Program, kalıp { eylem } çiftlerinden oluşur. Kayıt kalıba uyuyorsa eylem çalışır.

awk '$9 >= 500 { print $1, $7, $9 }' erisim.log
192.168.1.5 /api/veri 500
192.168.1.5 /api/veri 500
10.0.2.7 /api/veri 500

İki bölüm de isteğe bağlıdır ve eksik olan yerine varsayılan konur:

  • Kalıp yazılmazsa eylem her kayda uygulanır.
  • Eylem yazılmazsa varsayılan eylem { print } olur.
awk '$9 == 404' erisim.log | grep -c ''
4

Kalıp bir düzenli ifade de olabilir. /kalıp/ tüm kaydı, $9 ~ /kalıp/ belirli bir alanı sınar:

awk '$9 ~ /^[45]/ { n++ } END { print n }' erisim.log

Programın tamamı tek tırnak içinde yazılır. Programda $ işareti alan başvurusudur; çift tırnak kullanılırsa kabuk onu değişken genişletmesi sanar ve program bozulur. Bu, Değişkenler ve Tırnak Kuralları dersindeki kuralın en sık gerekli olduğu yerdir.

BEGIN ve END

BEGIN bloğu ilk kayıt okunmadan, END bloğu son kayıttan sonra çalışır. Başlık yazmak, ayar yapmak ve birikimleri raporlamak için kullanılır.

awk '
  BEGIN { print "adres            bayt" }
  { t += $10 }
  END { printf "toplam: %d bayt, %d istek, ortalama %.1f\n", t, NR, t / NR }
' erisim.log
adres            bayt
toplam: 117780 bayt, 30 istek, ortalama 3926.0

Değişkenler bildirilmeden kullanılır; ilk kullanımda sayısal bağlamda sıfır, dizgi bağlamında boş dizi değerindedirler. t += $10 satırı bu yüzden ayrı bir ilklendirme gerektirmedi.

printf biçimlendirmesi, kabuğun printf komutuyla aynı belirteçleri kullanır ve ondalıklı sayı basabilir — kabuk aritmetiğinin tam sayı kısıtı burada yoktur.

İlişkisel Diziler

Diziler yalnızca ilişkiseldir ve bildirim gerektirmez. Anahtar başına sayım, önceki konudaki kabuk döngüsünün tek satırlık karşılığıdır:

awk '{ sayim[$7]++ } END { for (y in sayim) printf "%3d %s\n", sayim[y], y }' erisim.log \
  | sort -rn | head -5
  6 /api/veri
  4 /yok.html
  4 /index.html
  3 /urun/45
  3 /urun/12

Anahtar başına toplam da aynı desendir:

awk '{ bayt[$7] += $10 } END { for (y in bayt) printf "%8d %s\n", bayt[y], y }' erisim.log \
  | sort -rn | head -5
   36120 /panel
   25230 /urun/12
   21990 /urun/45
   20480 /index.html
    7236 /api/veri

for (anahtar in dizi) döngüsünün ürettiği sıra tanımsızdır; sıralı çıktı için dışarıdan sort gerekir. Bu, ilişkisel dizilerin karma tablosuyla gerçekleştirilmesinin doğrudan sonucudur ve Veri Yapıları kursunda tanımlanmıştı.

Alan Ayırıcısını Değiştirmek

-F seçeneği alan ayırıcısını belirler. Değer tek karakter ya da düzenli ifade olabilir.

printf 'a:b:c\nd:e:f\n' | awk -F: '{ print $2 }'
b
e

Çıktı ayırıcısı ayrı bir değişkendir:

head -2 erisim.log | awk 'BEGIN { OFS="|" } { print $1, $7, $9 }'
10.0.0.12|/index.html|200
10.0.0.31|/statik/stil.css|200

print komutunda alanlar virgülle ayrılır; virgül OFS ile yer değiştirir. Virgül yerine boşluk yazılırsa alanlar ayırıcısız bitiştirilir — bu, dizgi birleştirmenin yazımıdır ve sessiz bir hata kaynağıdır.

Bir alana atama yapmak kaydı yeniden kurar:

head -1 erisim.log | awk '{ $1="GIZLI"; print }' | cut -c1-45
GIZLI - - [07/Feb/2024:09:12:44 +0000] "GET /

Kayıt yeniden kurulurken alanlar OFS ile birleştirilir; özgün satırdaki çoklu boşluklar korunmaz. Alan gizleme ve maskeleme işlemlerinde beklenen davranış budur.

Sayı ve Dizgi Bağlamı

Alanlar hem sayı hem dizgi olarak yorumlanabilir; hangi yorumun uygulanacağı işlecin türüne ve işlenenlerin kaynağına bağlıdır. Girdiden gelen bir alan sayı gibi görünüyorsa sayısal karşılaştırılır; ancak dizgi sabitleriyle karşılaştırıldığında dizgi bağlamı kazanabilir.

awk 'BEGIN { x="10"; y="9"; print (x   > y  ) ? "dizgi: 10>9" : "dizgi: 10<9" }'
awk 'BEGIN { x="10"; y="9"; print (x+0 > y+0) ? "sayi: 10>9"  : "sayi: 10<9"  }'
dizgi: 10<9
sayi: 10>9

Belirsizliği ortadan kaldırmanın yolu bağlamı açıkça zorlamaktır: sayısal karşılaştırma için +0, dizgi karşılaştırması için "" ile birleştirme. Dış kaynaklı veriyle çalışan programlarda bu alışkanlık, açıklaması güç sonuçları önler.

Kabuk Değişkeni Aktarmak

Kabuk değişkenleri program metnine gömülmez; -v seçeneğiyle aktarılır.

esik=400
awk -v esik="$esik" '$9 >= esik { n++ } END { print n " istek " esik " ve uzeri" }' erisim.log
9 istek 400 ve uzeri

Değeri program metnine gömmek — tırnakları kapatıp açarak — iki nedenle yanlıştır: değerdeki tırnak ve ters bölü karakterleri programı bozar, ve dışarıdan gelen bir değer program kodu hâline gelerek istenmeyen komutlar çalıştırabilir. -v bu sınırı korur.

Bozuk Kayıtları Yakalamak

Alan sayısı, biçim doğrulamasının en ucuz ölçütüdür:

awk 'NF != 10 { print FILENAME ":" FNR ": alan sayisi " NF }' tumu.log
tumu.log:31: alan sayisi 2
tumu.log:32: alan sayisi 9

Düzenli İfadeler dersinde aynı iş satırın tamamını betimleyen bir kalıpla yapılmıştı. Alan sayısı sınaması daha dar bir doğrulama yapar ama çok daha kısa yazılır ve biçim değişikliklerine dayanıklıdır. İkisi birlikte kullanılabilir: alan sayısı hızlı eleme, kalıp ayrıntılı doğrulama içindir.

Çok Kalıplı Program

Programda birden çok kalıp–eylem çifti bulunabilir; her kayıt tüm çiftlere karşı sırayla sınanır.

awk '
  $9 ~ /^2/     { basarili++ }
  $9 ~ /^[45]/  { hata++; hatali_yol[$7]++ }
  END {
    printf "basarili=%d hata=%d\n", basarili, hata
    for (y in hatali_yol) printf "  %-14s %d\n", y, hatali_yol[y]
  }
' erisim.log | sort
  /api/veri      3
  /gizli         2
  /yok.html      4
basarili=18 hata=9

Bir kayıt birden çok kalıba uyuyorsa ilgili tüm eylemler çalışır; case yapısındaki “ilk eşleşen dal” kuralı burada geçerli değildir.

Betiğe Uygulama

rapor.sh şu ana dek her sayım için kütüğü baştan okuyordu: satır sayımı, bayt toplamı ve dört durum sınıfı için altı ayrı geçiş. Hepsi tek geçişe iner:

kutuk=erisim.log

awk -v adet="$adet" '
  { istek++; bayt += $10; yol[$7]++; sinif[substr($9, 1, 1)]++ }
  END {
    printf "istek   : %d\n", istek
    printf "bayt    : %d\n", bayt
    printf "ortalama: %.1f bayt\n", (istek ? bayt / istek : 0)
    print ""
    print "durum kodu siniflari:"
    for (s = 2; s <= 5; s++)
      printf "  %sxx %4d  %%%.1f\n", s, sinif[s], (istek ? sinif[s] * 100 / istek : 0)
  }
' "$kutuk"
istek   : 30
bayt    : 117780
ortalama: 3926.0 bayt

durum kodu siniflari:
  2xx   18  %60.0
  3xx    3  %10.0
  4xx    6  %20.0
  5xx    3  %10.0

Üç kazanç var. Kütük bir kez okunuyor. Yüzdeler ondalıklı ve ölçeklendirme hilesi gerekmiyor. Sıfıra bölme koruması, kabuktaki üçlü işleçle aynı biçimde yazılıyor.

Kayıp da var: rapor mantığının bir bölümü artık kabukta değil, gömülü bir programda. Betiğin okunurluğu, bu programın kısa ve tek amaçlı kalmasına bağlıdır. İki yüz satırlık gömülü programlar, kabuk betiğinin sınırının aşıldığının işaretidir.

Özet

  • Girdi kayıtlara, kayıtlar alanlara bölünür; $0 kaydın tamamı, NF alan sayısı, NR kayıt sırasıdır. Öntanımlı ayırıcı boşluk kümeleridir.
  • Program kalıp { eylem } çiftlerinden oluşur; kalıp yoksa her kayda, eylem yoksa print uygulanır ve kayıt tüm çiftlere karşı sınanır.
  • BEGIN ve END blokları kayıt akışının dışında çalışır; değişkenler bildirimsizdir ve sıfırdan başlar.
  • İlişkisel dizilerle anahtar başına sayım ve toplam tek satırda yazılır; gezinme sırası tanımsızdır.
  • Kabuk değerleri program metnine gömülmez, -v ile aktarılır.
  • Sayı ve dizgi bağlamı işlenenlere bağlıdır; +0 ve "" ile açıkça zorlanır.

Sonraki Adım

Sayımlar üretildi ama sıralanmaları hep dış bir komuta bırakıldı. Sonraki ders o komutu ve yanındaki iki aracı ele alır: anahtara göre sıralama, komşu yinelemeleri tekilleştirme ve iki sıralı akışı küme işlemleriyle karşılaştırma — ayrıca sıralamanın yerel ayara bağlı olmasının betiklerde neden bir hata kaynağı olduğunu.

İ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