awk ile CSV Çıktısını HTML Tabloya Dönüştürme ve Raporlama

Sistem yöneticiliğinde raporlama işleri her zaman can sıkıcı bir yer kaplar. Birisi senden “şu sunucuların disk kullanım raporu lazım, bak bir bakar mısın” der, sen de terminale oturup df -h çıktısını kopyalayıp bir yerlere yapıştırmaya çalışırsın. Ya da log dosyalarından çektiğin verileri Excel’e aktarmak için onlarca adım geçersin. İşte tam burada awk devreye giriyor. CSV çıktısını alıp doğrudan HTML tabloya dönüştürmek, hem zaman kazandırıyor hem de patrona ya da ekibe sunulabilecek bir rapor ortaya çıkıyor. Bu yazıda sıfırdan bir HTML rapor üretme pipeline’ı kuracağız.

awk’ı Neden Bu İş İçin Kullanıyoruz?

Önce şunu soralım: Python var, Perl var, hatta csvkit gibi hazır araçlar bile var. Neden awk?

Çünkü awk neredeyse her Linux sistemde yüklü geliyor. Bir production sunucusuna bağlandığında Python 3.x yoktur, pip yoktur, sanal ortam kurmak için izin yoktur. Ama awk her zaman oradadır. Ayrıca metin akışı üzerinde çalışma konusunda awk‘ın sadeliği rakipsiz. Küçük ve anlaşılır script’ler yazıp tek satırda çalıştırabiliyorsun.

Bir de şu var: awk öğrenmek zaman alıyor ama bir kez kavradıktan sonra pek çok farklı senaryoda seni kurtarıyor. CSV’den HTML’e dönüşüm ise bu aracın gerçek gücünü gösterdiği en güzel örneklerden biri.

Temel awk Yapısı ve CSV Mantığı

awk‘ta alan ayırıcısını -F parametresiyle belirtiyoruz. CSV dosyaları için bu virgül oluyor:

awk -F',' '{print $1, $2, $3}' dosya.csv

Burada $1 birinci sütun, $2 ikinci sütun anlamına geliyor. $0 ise tüm satırı temsil ediyor. NR değişkeni satır numarasını, NF ise o satırdaki alan sayısını veriyor.

awk‘ta üç temel blok var:

  • BEGIN: Dosyayı okumaya başlamadan önce çalışır, HTML başlığı burada açılır
  • Ana blok: Her satır için çalışır, tablo satırları burada oluşturulur
  • END: Dosya bittikten sonra çalışır, HTML kapanış etiketleri burada yazılır

Bu yapıyı aklımızda tutarak ilk örneğimize geçelim.

İlk Örnek: Basit CSV’den HTML Tablosu

Elimizde şöyle bir sunucular.csv dosyası olduğunu varsayalım:

hostname,ip,os,durum
web01,192.168.1.10,Ubuntu 22.04,aktif
web02,192.168.1.11,Ubuntu 22.04,aktif
db01,192.168.1.20,CentOS 8,pasif
db02,192.168.1.21,CentOS 8,aktif
cache01,192.168.1.30,Debian 11,aktif

Bu dosyayı HTML tabloya dönüştüren en temel awk komutu:

awk -F',' '
BEGIN {
    print "<html><body>"
    print "<table border="1">"
}
NR==1 {
    print "<tr>"
    for(i=1; i<=NF; i++) print "<th>" $i "</th>"
    print "</tr>"
}
NR>1 {
    print "<tr>"
    for(i=1; i<=NF; i++) print "<td>" $i "</td>"
    print "</tr>"
}
END {
    print "</table>"
    print "</body></html>"
}
' sunucular.csv > rapor.html

Bu kadar. İlk satırı (NR==1) başlık satırı olarak etiketiyle, geri kalanları ise etiketiyle yazıyoruz. Döngü kullandığımız için sütun sayısından bağımsız çalışıyor.

CSS Ekleyerek Görsel Kalite Artırmak

Yukarıdaki çıktı işlevsel ama çirkin. Biraz CSS ekleyelim ve raporu gerçekten sunulabilir hale getirelim:

awk -F',' '
BEGIN {
    print "<!DOCTYPE html>"
    print "<html lang="tr">"
    print "<head><meta charset="UTF-8">"
    print "<title>Sunucu Envanter Raporu</title>"
    print "<style>"
    print "body { font-family: Arial, sans-serif; margin: 20px; }"
    print "table { border-collapse: collapse; width: 100%; }"
    print "th { background-color: #2c3e50; color: white; padding: 10px; text-align: left; }"
    print "td { padding: 8px; border: 1px solid #ddd; }"
    print "tr:nth-child(even) { background-color: #f2f2f2; }"
    print "tr:hover { background-color: #d5e8d4; }"
    print "h2 { color: #2c3e50; }"
    print "</style></head><body>"
    print "<h2>Sunucu Envanter Raporu</h2>"
    print "<table>"
}
NR==1 {
    print "<thead><tr>"
    for(i=1; i<=NF; i++) print "<th>" $i "</th>"
    print "</tr></thead><tbody>"
}
NR>1 {
    print "<tr>"
    for(i=1; i<=NF; i++) print "<td>" $i "</td>"
    print "</tr>"
}
END {
    print "</tbody></table>"
    print "</body></html>"
}
' sunucular.csv > rapor.html

Zebra şeritleme için nth-child(even), hover efekti için tr:hover ekledik. Bu haliyle tarayıcıda açıldığında gayet profesyonel duran bir tablo elde ediyorsun.

Gerçek Dünya Senaryosu: Disk Kullanım Raporu

Peki bunu gerçek sistem verisiyle nasıl kullanırsın? df çıktısını önce CSV formatına çevirip sonra HTML’e dönüştürelim:

df -h | grep -v tmpfs | awk '
NR==1 { print "filesystem,boyut,kullanilan,bos,yuzde,mount" }
NR>1  { print $1","$2","$3","$4","$5","$6 }
' | awk -F',' '
BEGIN {
    print "<!DOCTYPE html><html><head><meta charset="UTF-8">"
    print "<title>Disk Kullanim Raporu</title>"
    print "<style>"
    print "body{font-family:monospace;margin:30px;background:#1a1a2e;color:#eee}"
    print "table{border-collapse:collapse;width:100%}"
    print "th{background:#16213e;color:#0f3460;color:#e94560;padding:12px}"
    print "td{padding:10px;border:1px solid #333}"
    print "tr:nth-child(even){background:#16213e}"
    print ".uyari{color:#f39c12;font-weight:bold}"
    print ".kritik{color:#e74c3c;font-weight:bold}"
    print "</style></head><body>"
    print "<h2>Disk Kullanim Raporu - " strftime("%d.%m.%Y %H:%M") "</h2>"
    print "<table><thead><tr>"
}
NR==1 {
    split($0, basliklar, ",")
    for(i=1; i<=length(basliklar); i++) print "<th>" basliklar[i] "</th>"
    print "</tr></thead><tbody>"
}
NR>1 {
    yuzde = $5
    gsub(/%/, "", yuzde)
    sinif = ""
    if (yuzde+0 >= 90) sinif = "kritik"
    else if (yuzde+0 >= 75) sinif = "uyari"
    
    print "<tr>"
    for(i=1; i<=NF; i++) {
        if (i==5 && sinif != "") print "<td class="" sinif "">" $i "</td>"
        else print "<td>" $i "</td>"
    }
    print "</tr>"
}
END {
    print "</tbody></table></body></html>"
}
' > disk_raporu_$(date +%Y%m%d).html

Bu script’in güzel yanı şu: Disk kullanımı yüzde 75’in üzerindeyse sarı uyarı, yüzde 90’ın üzerindeyse kırmızı kritik renklendirme yapıyor. Raporu cron’a bağlayıp her sabah e-posta ile gönderebilirsin.

awk Script Dosyası Kullanımı

Komutlar uzadıkça tek satırda yazmak anlamsızlaşıyor. Script’i bir dosyaya alman daha mantıklı:

# rapor.awk dosyasi
BEGIN {
    FS = ","
    print "<!DOCTYPE html>"
    print "<html><head><meta charset="UTF-8">"
    printf "<title>%s</title>n", baslik
    print "<style>body{font-family:sans-serif}table{width:100%;border-collapse:collapse}"
    print "th{background:#34495e;color:white;padding:10px}td{padding:8px;border:1px solid #ccc}"
    print "tr:nth-child(even){background:#ecf0f1}</style></head><body>"
    printf "<h1>%s</h1>n", baslik
    print "<table>"
    satir_sayisi = 0
}
NR==1 {
    print "<thead><tr>"
    for(i=1; i<=NF; i++) printf "<th>%s</th>", $i
    print "</tr></thead><tbody>"
}
NR>1 {
    satir_sayisi++
    print "<tr>"
    for(i=1; i<=NF; i++) printf "<td>%s</td>", $i
    print "</tr>"
}
END {
    printf "</tbody></table><p>Toplam kayit: <strong>%d</strong></p>", satir_sayisi
    printf "<p><small>Olusturulma: %s</small></p>", strftime("%d.%m.%Y %H:%M:%S")
    print "</body></html>"
}

Bunu şöyle çalıştırıyorsun:

awk -v baslik="Sunucu Envanteri" -f rapor.awk sunucular.csv > cikti.html

-v parametresiyle dışarıdan değişken geçirebiliyorsun. Bu sayede aynı script’i farklı başlıklarla farklı CSV dosyaları için kullanabilirsin.

Log Dosyasından CSV Üretip HTML’e Dönüştürmek

Çoğu zaman elinizde hazır bir CSV olmaz, log dosyasından üretmeniz gerekir. Nginx access log’undan en çok istek atan IP’leri raporlayan bir örnek yapalım:

# Once log'dan IP frekansi CSV'si olustur
awk '{print $1}' /var/log/nginx/access.log | 
sort | uniq -c | sort -rn | head -20 | 
awk '{print $2","$1}' | 
awk -v tarih="$(date '+%d.%m.%Y')" '
BEGIN {
    FS=","
    print "<!DOCTYPE html><html><head><meta charset="UTF-8">"
    print "<style>"
    print "body{font-family:Arial;margin:20px;background:#f5f5f5}"
    print ".kutu{background:white;padding:20px;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1)}"
    print "table{width:100%;border-collapse:collapse;margin-top:15px}"
    print "th{background:#2980b9;color:white;padding:12px;text-align:left}"
    print "td{padding:10px;border-bottom:1px solid #eee}"
    print "tr:first-child th{border-radius:4px 4px 0 0}"
    print ".bar{background:#3498db;height:18px;border-radius:3px}"
    print "</style></head><body><div class="kutu">"
    print "<h2>Nginx - En Cok Istek Atan IP Adresleri</h2>"
    printf "<p>Rapor tarihi: %s</p>n", tarih
    print "<table><thead><tr><th>IP Adresi</th><th>Istek Sayisi</th><th>Gorsel</th></tr></thead><tbody>"
    max_istek = 0
}
NR==1 { max_istek = $2 }
{
    if (max_istek > 0) yuzde = int(($2 / max_istek) * 200)
    else yuzde = 0
    printf "<tr><td>%s</td><td>%s</td>", $1, $2
    printf "<td><div class="bar" style="width:%dpx"></div></td></tr>n", yuzde
}
END {
    print "</tbody></table></div></body></html>"
}
' > nginx_ip_raporu.html

Bu script’te bar chart efekti de ekledik. Her IP’nin istek sayısını, en yüksek değere göre normalize edip görsel bir çubuk olarak gösteriyoruz. Tamamen HTML/CSS, hiç JavaScript yok.

Koşullu Renklendirme ve Veri Doğrulama

Raporlarda en kullanışlı özelliklerden biri, belirli değerlerin öne çıkarılması. Mesela bir uptime raporunda yüzde 99’un altındaki değerleri kırmızı yapmak isteyebilirsin:

# uptime.csv ornegi:
# servis,uptime_yuzde,son_kesinti,sorumlu
# web-api,99.95,2024-01-05,[email protected]
# payment-svc,98.20,2024-01-12,[email protected]
# auth-svc,100,hiç,[email protected]

awk -F',' '
BEGIN {
    print "<!DOCTYPE html><html><head><meta charset="UTF-8">"
    print "<style>"
    print "body{font-family:Arial;margin:25px}"
    print "table{border-collapse:collapse;width:100%}"
    print "th{background:#2c3e50;color:white;padding:12px;text-align:center}"
    print "td{padding:10px;border:1px solid #ddd;text-align:center}"
    print ".iyi{background:#d5f5e3;color:#1e8449;font-weight:bold}"
    print ".orta{background:#fef9e7;color:#d68910;font-weight:bold}"
    print ".kotu{background:#fdedec;color:#c0392b;font-weight:bold}"
    print ".mukemmel{background:#eaf2ff;color:#1a5276;font-weight:bold}"
    print "</style></head><body>"
    print "<h2>Servis Uptime Raporu</h2><table><thead><tr>"
}
NR==1 {
    for(i=1; i<=NF; i++) print "<th>" $i "</th>"
    print "<th>Durum</th></tr></thead><tbody>"
}
NR>1 {
    uptime = $2 + 0
    if (uptime == 100) { sinif = "mukemmel"; etiket = "Mukemmel" }
    else if (uptime >= 99.9) { sinif = "iyi"; etiket = "Iyi" }
    else if (uptime >= 99)   { sinif = "orta"; etiket = "Dikkat" }
    else                     { sinif = "kotu"; etiket = "Kritik!" }
    
    print "<tr>"
    for(i=1; i<=NF; i++) {
        if (i==2) printf "<td class="%s">%%%s</td>", sinif, $i
        else printf "<td>%s</td>", $i
    }
    printf "<td class="%s">%s</td></tr>n", sinif, etiket
}
END {
    print "</tbody></table></body></html>"
}
' uptime.csv > uptime_raporu.html

Birden Fazla CSV’yi Tek Raporda Birleştirmek

Büyük ortamlarda tek bir CSV yetmez. Farklı kaynaklardan gelen verileri tek HTML raporunda birleştirmek isteyebilirsin:

#!/bin/bash
# coklu_rapor.sh

TARIH=$(date '+%d.%m.%Y %H:%M')
CIKTI="ozet_rapor_$(date +%Y%m%d).html"

# HTML basligi
cat > $CIKTI << 'EOF'
<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>Sistem Ozet Raporu</title>
<style>
body{font-family:Arial;margin:20px;background:#ecf0f1}
.bolum{background:white;margin:20px 0;padding:20px;border-radius:8px;box-shadow:0 2px 6px rgba(0,0,0,0.1)}
h2{color:#2c3e50;border-bottom:2px solid #3498db;padding-bottom:8px}
table{width:100%;border-collapse:collapse}
th{background:#34495e;color:white;padding:10px}
td{padding:8px;border:1px solid #ddd}
tr:nth-child(even){background:#f8f9fa}
</style></head><body>
EOF

echo "<h1>Sistem Ozet Raporu - $TARIH</h1>" >> $CIKTI

# Disk bolumu
echo '<div class="bolum"><h2>Disk Kullanimi</h2>' >> $CIKTI
df -h | grep -v tmpfs | tail -n +2 | 
awk 'BEGIN{print "<table><tr><th>Dosya Sistemi</th><th>Boyut</th><th>Kullanilan</th><th>Bos</th><th>%</th><th>Mount</th></tr>"}
{print "<tr><td>"$1"</td><td>"$2"</td><td>"$3"</td><td>"$4"</td><td>"$5"</td><td>"$6"</td></tr>"}
END{print "</table>"}' >> $CIKTI
echo '</div>' >> $CIKTI

# Bellek bolumu
echo '<div class="bolum"><h2>Bellek Kullanimi</h2>' >> $CIKTI
free -h | awk 'BEGIN{print "<table><tr><th>Tur</th><th>Toplam</th><th>Kullanilan</th><th>Bos</th></tr>"}
NR>1{print "<tr><td>"$1"</td><td>"$2"</td><td>"$3"</td><td>"$4"</td></tr>"}
END{print "</table>"}' >> $CIKTI
echo '</div>' >> $CIKTI

# Kapanisi
echo '</body></html>' >> $CIKTI

echo "Rapor olusturuldu: $CIKTI"

Bu script’i cron’a eklemek bir dakika:

# Her gun saat 07:00'de rapor olustur ve mail gonder
0 7 * * * /opt/scripts/coklu_rapor.sh && 
  mail -s "Gunluk Sistem Raporu" -a "Content-Type: text/html" 
  [email protected] < /opt/raporlar/ozet_rapor_$(date +%Y%m%d).html

Önemli awk Parametreleri ve Notlar

Script’lerde sık kullandığım bazı awk özelliklerini not düşeyim:

  • FS: Alan ayırıcısı (Field Separator), BEGIN içinde FS="," şeklinde de ayarlanabilir
  • NR: Toplam satır numarası (Number of Records)
  • NF: O satırdaki alan sayısı (Number of Fields)
  • OFS: Çıktı alan ayırıcısı (Output Field Separator)
  • strftime: Tarih formatlama fonksiyonu, gawk‘ta çalışır, standart awk‘ta olmayabilir
  • gsub(regex, yeni, degisken): Global değiştirme, tüm eşleşmeleri değiştirir
  • sub(regex, yeni, degisken): Sadece ilk eşleşmeyi değiştirir
  • split(string, dizi, ayirici): String’i diziye böler
  • printf: C-stili formatlı çıktı için, HTML üretirken çok işe yarıyor

Dikkat edilmesi gereken bir nokta: Bazı sistemlerde strftime için gawk gerekiyor. Ubuntu ve modern dağıtımlarda awk zaten gawk‘a symlink edilmiş oluyor. Eğer değilse gawk paketini kur.

Ayrıca CSV içindeki virgüllü değerlerde (örneğin "New York, USA" gibi tırnak içi virgüller) basit awk çözümü sorun çıkarabilir. Bu durumda ya değerleri önceden temizlemen ya da FPAT kullanman gerekiyor:

# Tirnak icindeki virgulleri dogru parse etmek icin
gawk 'BEGIN { FPAT = "([^,]+)|("[^"]+")" } { print $1, $2 }' dosya.csv

Sonuç

awk ile CSV’den HTML rapor üretmek, sistem yöneticisinin araç kutusundaki en değerli becerilerden biri. Hiçbir ek paket kurmadan, sadece birkaç satır script ile yöneticiye, müşteriye ya da ekibe sunulabilecek kalitede raporlar üretebiliyorsun.

Burada anlattıklarımı kendi ortamına uyarlarken şu yolda ilerlemeni öneririm: Önce en basit haliyle başla, düz HTML tablosu üret. Sonra CSS ekle. Sonra koşullu renklendirmeyi dene. En son cron’a bağla ve e-posta gönderimini kur. Her adımda çıktıyı tarayıcıda açıp kontrol et.

Gerçek işe yararlılığı şurada: Bir kez kurduğunda haftalar boyunca dokunmadan çalışıyor. Sabah işe geldiğinde bir e-postada disk raporu, uptime raporu, güvenlik olayları raporu seni bekliyor. Bunları manuel toplamak yerine awk senin için yapıyor. Zamanın daha değerli işlere kalıyor.

Bir yanıt yazın

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