awk ile Yinelenen Satırları Tespit Etme, Gruplama ve Frekans Analizi

Logları analiz ederken, access log’lardan duplicate kayıtları temizlerken ya da CSV dosyalarındaki tekrarlayan satırları bulmaya çalışırken “keşke bunu daha pratik yapmanın bir yolu olsa” diye düşünmüşsünüzdür. İşte tam burada awk devreye giriyor. Çoğu sysadmin awk‘ı sadece belirli sütunları kesmek için kullanır, ama bu araç aslında yinelenen satırları tespit etmek, gruplamak ve frekans analizi yapmak için son derece güçlü bir silah.

Bu yazıda gerçek dünya senaryolarından yola çıkarak, log analizi ve veri temizliği bağlamında awk‘ın bu yeteneklerini derinlemesine inceleyeceğiz.

Temel Mantığı Anlamak: awk’ta Associative Array

awk‘ın yinelenen satır tespiti için bu kadar iyi olmasının sebebi, dahili olarak associative array (ilişkilendirmeli dizi) desteğine sahip olması. Yani bir satırı ya da alanı anahtar olarak kullanıp, kaç kez karşılaştığınızı sayabiliyorsunuz.

En temel hali şöyle:

awk '{ count[$0]++ } END { for (line in count) print count[line], line }' dosya.txt

Bu tek satır şunu yapıyor: Her satırı ($0) anahtar olarak kullanarak bir sayaç tutuyor. Dosya bitince tüm anahtarları ve sayıları ekrana basıyor. Çıktı sıralamalı gelmeyebilir, bunu şöyle düzeltebilirsiniz:

awk '{ count[$0]++ } END { for (line in count) print count[line], line }' dosya.txt | sort -rn

sort -rn ile en çok tekrar eden satırlar üste gelir. Özellikle log analizi yaparken bu sıralama kritik öneme sahip.

Sadece Yinelenen Satırları Göstermek

Bazen bütün frekans tablosunu değil, sadece birden fazla kez geçen satırları görmek istersiniz. Bu durumda filtre eklemek gerekiyor:

awk '{ count[$0]++ } END { for (line in count) if (count[line] > 1) print count[line], line }' dosya.txt | sort -rn

Tersini de yapabilirsiniz, yani sadece bir kez geçen (unique) satırları bulmak için:

awk '{ count[$0]++ } END { for (line in count) if (count[line] == 1) print line }' dosya.txt

Bu özellikle iki farklı sistem arasındaki farkları bulurken işe yarıyor. Mesela iki farklı sunucudan aldığınız kullanıcı listesini karşılaştırıyorsunuz ve sadece bir sistemde var olan kullanıcıları bulmak istiyorsunuz.

Belirli Bir Alanı Temel Alarak Yineleme Tespiti

Gerçek dünyada her zaman tüm satırı değil, belirli bir sütunu kontrol etmek istersiniz. Nginx access log’larını düşünelim. IP adresine göre kaç istek geldiğini görmek istiyorsanız:

awk '{ count[$1]++ } END { for (ip in count) print count[ip], ip }' /var/log/nginx/access.log | sort -rn | head -20

Burada $1 ilk alanı, yani IP adresini temsil ediyor. Bu çıktı size potansiyel bir DDoS kaynağını veya kötü niyetli bir tarayıcıyı birkaç saniyede gösterebilir. Bunu ürün ortamında defalarca kullandım ve bir günde 50.000’den fazla istek atan IP adreslerini bu şekilde tespit ettim.

Birden fazla alanı birleştirerek de kullanabilirsiniz. Örneğin hem IP hem de istenen URL kombinasyonuna göre:

awk '{ count[$1" "$7]++ } END { for (key in count) print count[key], key }' /var/log/nginx/access.log | sort -rn | head -20

Yinelenen Satırları Kaldırmak (Sıra Korunarak)

uniq komutu sadece ardışık tekrarları kaldırır, bu yüzden önce sort atmanız gerekir ve bu sırayı bozar. Ama awk ile sırayı koruyarak duplicate’leri kaldırabilirsiniz:

awk '!seen[$0]++' dosya.txt

Bu muhtemelen awk dünyasının en zarif tek satırlarından biri. Şöyle çalışıyor: seen[$0] değeri 0 (yani görülmemiş) ise !seen[$0] ifadesi true olur ve satır yazdırılır. Aynı zamanda değer increment edilir. İkinci kez aynı satır geldiğinde seen[$0] artık 1’dir, !1 false olur ve satır atlanır.

Belirli bir alana göre duplicate kaldırmak için:

awk '!seen[$3]++' dosya.txt

Bu özellikle CSV ya da TSV dosyalarında çok işe yarıyor. Örneğin kullanıcı ID’sine göre ilk kaydı tutup tekrarları atmak istiyorsanız.

Gerçek Dünya Senaryosu: Apache Log Analizi

Diyelim ki bir Apache log dosyası var ve 404 dönen URL’lerin frekans analizini yapmak istiyorsunuz. 404 hatalarını filtrele, URL’yi al, say ve sırala:

awk '$9 == "404" { count[$7]++ } END { for (url in count) print count[url], url }' /var/log/apache2/access.log | sort -rn | head -30

Burada $9 HTTP durum kodunu, $7 ise istenen URL’yi temsil ediyor (Combined Log Format’a göre). Bu çıktı size en çok 404 dönen endpoint’leri gösterir. Broken link mi var, eski API endpoint mi hala çağrılıyor, bunu anında görürsünüz.

Bir adım ileri giderek, belirli bir saat dilimindeki hataları analiz edebilirsiniz:

awk '$9 == "500" && $4 ~ /[10/Jan/2025:14/ { count[$7]++ } END { for (url in count) print count[url], url }' /var/log/apache2/access.log | sort -rn

Bu komut sadece 10 Ocak 2025, saat 14:xx arasındaki 500 hatalarını filtreler. Bir deployment sonrası belirli bir zaman diliminde ne olduğunu anlamak için biçilmiş kaftan.

Çoklu Alan Gruplama ile Frekans Matrisi

Bazen tek boyutlu analiz yetmez. Örneğin hangi IP’nin hangi HTTP metodunu kaç kez kullandığını görmek istiyorsunuz:

awk '{ count[$1" "$6]++ } END { for (key in count) print count[key], key }' /var/log/nginx/access.log | sort -rn

Burada $6 HTTP metodunu içeriyor (GET, POST, PUT, DELETE). Çıktı şuna benzer:

15234 192.168.1.100 "GET
8921 192.168.1.101 "POST
234 10.0.0.50 "DELETE

Bu analiz, bir API servisinin kullanım pattern’ini anlamak için son derece değerli. Özellikle rate limiting kuralları belirlerken bu verilere ihtiyaç duyuyorsunuz.

Satır Numarası ile Birlikte Yineleme Tespiti

Büyük yapılandırma dosyalarında ya da script’lerde bir değerin kaç farklı yerde geçtiğini ve tam olarak nerede olduğunu bulmak isteyebilirsiniz:

awk '{ lines[$0] = lines[$0] (lines[$0] ? "," : "") NR; count[$0]++ } END { for (line in count) if (count[line] > 1) print count[line], "satir(lar):" lines[line], "->", line }' dosya.txt

Bu biraz daha karmaşık ama son derece faydalı. Her satır için hem sayıyı hem de hangi satır numaralarında geçtiğini tutuyor. Büyük bir Ansible playbook’ta aynı task’ın birden fazla yerde tanımlanıp tanımlanmadığını bulmak için bu tekniği kullandım.

CSV Dosyalarında Alan Bazlı Frekans Analizi

CSV dosyalarıyla çalışırken field separator’ı değiştirmeniz gerekiyor:

awk -F',' '{ count[$3]++ } END { for (val in count) print count[val], val }' veri.csv | sort -rn

Burada -F',' virgülü ayraç olarak tanımlıyor ve $3 üçüncü sütunu alıyor. Örneğin bir kullanıcı veritabanı export’unda şehir sütununa göre kullanıcı dağılımını görmek için kullanabilirsiniz.

Başlık satırını atlamak istiyorsanız:

awk -F',' 'NR>1 { count[$3]++ } END { for (val in count) print count[val], val }' veri.csv | sort -rn

NR>1 koşulu ilk satırı (başlık) atlıyor.

Birden fazla sütunu birleştirip analiz etmek de mümkün:

awk -F',' 'NR>1 { key=$2 FS $3; count[key]++ } END { for (k in count) print count[k], k }' veri.csv | sort -rn

Bu şekilde örneğin “şehir + meslek” kombinasyonuna göre kullanıcı sayısını bulabilirsiniz.

Yüzde Hesabı ile Frekans Raporu

Sadece sayı değil, yüzde de görmek isteyebilirsiniz. Bu özellikle raporlama yaparken çok işe yarıyor:

awk '{ count[$1]++; total++ } END { for (ip in count) printf "%.2f%% (%d)t%sn", (count[ip]/total)*100, count[ip], ip }' /var/log/nginx/access.log | sort -rn | head -15

Bu komut her IP’nin toplam trafik içindeki yüzdesini de gösteriyor. Bir müşteriye “bu IP toplam trafiğin %23’ünü oluşturuyor” demek, “bu IP 45000 istek atmış” demekten çok daha etkili oluyor.

Zaman Bazlı Gruplama: Saate Göre Trafik Dağılımı

Log analizi yaparken zaman bazlı gruplama çok değerli bilgiler sunar. Nginx loglarında saate göre trafik dağılımını görmek için:

awk '{ match($4, /[0-9]{2}:[0-9]{2}/, arr); hour=substr(arr[0],1,2); count[hour]++ } END { for (h in count) printf "%s:00 -> %d istekn", h, count[h] }' /var/log/nginx/access.log | sort

Ya da daha basit bir yaklaşımla, tarih alanını parse ederek:

awk '{ split($4, t, ":"); hour=t[2]; count[hour]++ } END { for (h in count) print count[h], h":00" }' /var/log/nginx/access.log | sort -k2

Bu çıktı size günün hangi saatinde en fazla trafik geldiğini gösterir. Buna bakarak maintenance window’ınızı planlayabilir, auto-scaling threshold’larınızı ayarlayabilirsiniz.

Sistem Log’larında Hata Tespiti

/var/log/syslog ya da journalctl çıktısında belirli hata mesajlarının frekansını analiz etmek:

awk '/ERROR|WARN|CRITICAL/ { 
    match($0, /(ERROR|WARN|CRITICAL)/, type)
    count[type[0]]++ 
} END { 
    for (t in count) print count[t], t 
}' /var/log/syslog

Bu komut log seviyesine göre gruplama yapıyor. Hangi seviyeden kaç tane mesaj üretildiğini tek bakışta görebiliyorsunuz.

Servis adına göre gruplama yapmak için:

journalctl --no-pager -p err | awk '{ count[$5]++ } END { for (svc in count) print count[svc], svc }' | sort -rn | head -10

Hangi servisin en çok hata ürettiğini bu şekilde anında bulabilirsiniz.

Büyük Dosyalarda Performans İpuçları

awk zaten oldukça hızlı bir araç, ama çok büyük log dosyalarıyla çalışırken birkaç optimizasyon yapılabilir.

Dosyayı önce filtrele, sonra awk‘a ver:

grep "ERROR" /var/log/uygulama.log | awk '{ count[$5]++ } END { for (k in count) print count[k], k }' | sort -rn

grep‘in çıktısını pipe ile awk‘a vermek, awk‘ın tüm dosyayı işlemesinden daha hızlı olabilir çünkü grep oldukça optimize edilmiş.

Çoklu dosyayı aynı anda işlemek için:

awk '{ count[$1]++ } END { for (ip in count) print count[ip], ip }' /var/log/nginx/access.log* | sort -rn

awk wildcard ile birden fazla dosyayı tek seferde işleyebilir, bu da büyük bir kolaylık.

Pratik Bir Script: Günlük Log Özet Raporu

Tüm bu teknikleri bir araya getiren küçük bir script yazalım. Bu script her gün çalıştırılabilir ve nginx loglarından özet rapor üretebilir:

#!/bin/bash

LOG=/var/log/nginx/access.log
TARIH=$(date '+%Y-%m-%d')

echo "=== Nginx Log Analizi: $TARIH ==="
echo ""

echo ">> En Cok Istek Atan IP'ler (Top 10):"
awk '{ count[$1]++; total++ } END { 
    for (ip in count) 
        printf "  %.1f%% - %d istek - %sn", (count[ip]/total)*100, count[ip], ip 
}' $LOG | sort -rn | head -10

echo ""
echo ">> HTTP Hata Kodlari:"
awk '$9 ~ /^[45]/ { count[$9]++ } END { 
    for (code in count) 
        print "  HTTP", code, "->", count[code], "kez" 
}' $LOG | sort -k3 -rn

echo ""
echo ">> En Cok 404 Veren URL'ler (Top 5):"
awk '$9 == "404" { count[$7]++ } END { 
    for (url in count) 
        print "  " count[url], url 
}' $LOG | sort -rn | head -5

echo ""
echo ">> Saatlik Trafik Dagilimi:"
awk '{ split($4,t,":"); count[t[2]]++ } END { 
    for (h in count) 
        printf "  Saat %s -> %d istekn", h, count[h] 
}' $LOG | sort -k2

Bu script’i crontab’a ekleyerek her sabah mail ile alabilirsiniz:

0 8 * * * /usr/local/bin/log_analiz.sh | mail -s "Gunluk Log Raporu" [email protected]

Sonuç

awk‘ın associative array mekanizması, yinelenen satır tespiti ve frekans analizi için gerçekten güçlü bir temel sunuyor. Burada ele aldığımız teknikler şunlar:

  • Temel frekans sayımı: count[$0]++ mantığı ile her satırın kaç kez geçtiğini bulmak
  • Alan bazlı gruplama: $1, $3 gibi belirli sütunları anahtar olarak kullanmak
  • Sıra koruyan duplicate kaldırma: !seen[$0]++ idiom’u
  • Yüzde hesabı: Toplam sayıyı takip edip oransal analiz yapmak
  • Zaman bazlı gruplama: Log timestamp’lerini parse ederek saatlik/günlük analiz
  • Çoklu alan kombinasyonu: Birden fazla sütunu birleştirerek daha detaylı gruplama

Bu araçları log analizi, güvenlik tespiti, kapasite planlaması ve veri temizliği gibi pek çok farklı senaryoda kullanabilirsiniz. awk öğrenmesi biraz zaman alan ama bir kez kavradığınızda vazgeçemeyeceğiniz bir araç. Özellikle Python veya başka bir dil gerektirmeyen, sadece terminal ve standart araçlarla çözüm üretme gerektiren durumlarda bu bilgi gerçekten hayat kurtarıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir