00 Neden üçü birlikte
Üçü farklı soruları yanıtlar — ama aynı pipeline'da yan yana çalışırlar.
Unix metin araçları tek bir şeyi iyi yapar prensibiyle tasarlanmıştır. Bu üç araç da bu felsefeyi somutlaştırır:
grep → satır filtreler "bunu içeren satırları ver" sed → satır dönüştürür "şunu şuna çevir, sil, ekle" awk → satırı programlar "alanları parçala, hesapla, raporla"
Bir işlemi yapabilmek için tek bir araç yeterli olmayabilir. Ama üçünü zincirlediğinde neredeyse her metin dönüşümünü gerçekleştirebilirsin — Python veya Perl yazmadan.
grep: regex ile satır seç → sed: seçilen satırı düzenle → awk: satırı alanlarına ayır, hesapla. Üçü birbirine pipe ile bağlanır; her biri bir öncekinin çıktısını alır.
Bu rehberde aynı nginx access log dosyasını üçüyle işleyeceğiz. Böylece hangisinin ne işe yaradığını soyut değil, somut olarak göreceksin.
01 grep — temel kullanım
grep (Global Regular Expression Print) — bir dosyada ya da stdin'de pattern'e uyan satırları döker.
Temel sözdizimi
grep 'pattern' dosya.txt
grep 'pattern' dosya1.txt dosya2.txt
grep 'pattern' *.log
# stdin'den oku
cat access.log | grep '404'
# birden fazla satır üret (grep sonrası awk için klasik):
grep 'ERROR' app.log | grep -v 'timeout'
Sık kullanılan flagler
grep -i 'error' → ERROR, Error, error hepsini yakalar.grep -v 'DEBUG' → DEBUG içermeyen satırlar.grep -r 'TODO' src/grep -rl 'deprecated' .grep -w 'log' → "log" bulur ama "logger" bulmaz.+ ? | () için ters eğik çizgi gerekmez). Alias: egrep.fgrep.Pratik örnekler
# 404 olan satırlar, satır numarasıyla
grep -n '404' access.log
# "error" içeren ama "disk" içermeyen log satırları
grep -i 'error' syslog | grep -iv 'disk'
# src/ altında "TODO" geçen tüm dosyaların adı
grep -rl 'TODO' src/
# "WARN" kaç satırda var?
grep -c 'WARN' app.log
# hata satırı ve sonraki 3 satırı göster (stack trace için)
grep -A 3 'Exception' app.log
grep -n '404' access.log — dosyayı satır satır okur, "404" içeren her satırı satır_no:içerik formatında yazar. Geri kalanları siler. Çıktı saf metin, bir sonraki pipe'a hazır.
02 grep — regex derinlemesi
grep iki farklı regex motoru sunar: Basic (BRE) ve Extended (ERE). Fark yalnızca sözdizimsel.
BRE vs ERE
+ ? | () {} özel değil — kullanmak için önlerine \ koy: \+ \| \(+ ? | () doğrudan özel — literal kullanmak için \ koy: \. \+\d \s (?:...) gibi Perl uzantıları. GNU grep'te var ama macOS grep'te yok (homebrew grep gerektirir).Sık kullanılan meta-karakterler
^ERROR → "ERROR" ile başlayan satırlar.\.log$ → ".log" ile biten satırlar.cat|dog → cat veya dog.(ha)+ → ha, haha, hahaha…[0-9]{3,5} → 3 ile 5 basamak arası.Örnekler
# IPv4 adresi yakala (kaba, ama işe yarar)
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' access.log
# 4xx veya 5xx HTTP kodları
grep -E ' (4|5)[0-9]{2} ' access.log
# boş olmayan satırlar
grep -v '^$' dosya.txt
# "func" ile başlayan satır (Go kaynak kodu)
grep -rn '^func ' --include='*.go' .
# "error" veya "warn" geçen satırlar (büyük/küçük harf fark etmez)
grep -iE 'error|warn' syslog
grep '1.2.3.4' yazdığında nokta "herhangi karakter" anlamına gelir: "1X2Y3Z4" de eşleşir. IP aramalarında \. veya -F '1.2.3.4' kullan.
grep -rn 'pattern' --include='*.py' . — sadece .py dosyalarında ara. --exclude='*.min.js' ile belirli dosyaları dışarıda bırak.
03 sed — s/// ikamesi ve address'ler
sed (Stream EDitor) satırları dönüştürür. Kayıttan okuduğu her satıra bir veya daha fazla komut uygular, sonucu yazar.
s/// — ikame komutu
# s/pattern/değiştirme/flags
sed 's/foo/bar/' dosya.txt # her satırda ilk foo'yu bar yap
sed 's/foo/bar/g' dosya.txt # her satırda TÜM foo'ları bar yap
sed 's/foo/bar/2' dosya.txt # her satırda sadece 2. foo'yu değiştir
sed 's/foo/bar/gi' dosya.txt # global + case-insensitive
sed 's/foo/bar/gp' dosya.txt # değişen satırı bir de ekstra yaz (p)
I kullan ya da homebrew gsed kur.-n ile birlikte — "sadece değişen satırları göster".Address — hangi satıra uygulanacak
sed komutları varsayılan olarak her satıra uygulanır. Önüne bir address yazarsın, yalnızca o satır(lar)a uygulanır.
# Satır numarasıyla address
sed '1s/foo/bar/' dosya.txt # sadece 1. satırda
sed '2,5s/foo/bar/g' dosya.txt # 2. ile 5. satır arası
sed '$s/foo/bar/' dosya.txt # son satırda
# Pattern address
sed '/^#/s/old/new/g' dosya.txt # # ile başlayan satırlarda
sed '/^#/!s/old/new/g' dosya.txt # # ile BAŞLAMAYAN satırlarda (! = negation)
# Aralık (pattern'den pattern'e)
sed '/START/,/END/s/foo/bar/g' d.txt # START'tan END'e kadar
sed '3,/END/s/foo/bar/g' d.txt # 3. satırdan END pattern'ine kadar
/^#/!s/old/new/g — ! işareti address'i negete eder: "# ile başlamayan her satırda s komutunu çalıştır". Yorum satırlarını atlayarak kodu düzenlemenin klasik yolu.
Sınırlayıcı değiştirme
Pattern veya replacement içinde / varsa, farklı bir sınırlayıcı kullanabilirsin — sed her karakteri kabul eder:
# /path içeren şeyi değiştirmek zor (\/ yazmak yerine | kullan)
sed 's|/usr/local/bin|/opt/bin|g' script.sh
# # ile de olur
sed 's#http://old.com#https://new.com#g' config.txt
04 sed — inplace düzenleme ve diğer komutlar
sed varsayılan olarak sadece stdout'a yazar, orijinal dosyaya dokunmaz. -i ile yerinde değiştirebilirsin.
-i ile inplace
# GNU sed (Linux): -i, ek uzantı gerekmez
sed -i 's/foo/bar/g' dosya.txt
# macOS sed: -i için boş string uzantısı gerekir
sed -i '' 's/foo/bar/g' dosya.txt
# Yedek al (GNU ve macOS'ta farklı)
sed -i.bak 's/foo/bar/g' dosya.txt # dosya.txt.bak yedek oluşturur
macOS, BSD sed kullanır. GNU sed, birçok yerde farklı davranır (-i sözdizimi, -E vs -r extended regex bayrağı, \w desteği). macOS'ta güvenilir olmak için brew install gnu-sed ve gsed kullan ya da her farkı bilerek yaz.
d — satır silme
# # ile başlayan satırları sil (yorum satırları)
sed '/^#/d' config.txt
# Boş satırları sil
sed '/^[[:space:]]*$/d' dosya.txt
# 1-5. satırları sil
sed '1,5d' dosya.txt
# Son satırı sil
sed '$d' dosya.txt
p — satır yazdırma, -n ile birlikte
# Sadece "ERROR" içeren satırları yazdır (grep gibi ama sed ile)
sed -n '/ERROR/p' app.log
# 10-20. satırları çıkar (head + tail yerine)
sed -n '10,20p' büyük_dosya.txt
a, i — satır ekleme
# [HOST] satırından SONRA yeni satır ekle
sed '/\[HOST\]/a\ server = 192.168.1.1' config.ini
# [HOST] satırından ÖNCE satır ekle
sed '/\[HOST\]/i\# Auto-generated' config.ini
# Birden fazla komut: -e veya ; ile zincirle
sed -e 's/foo/bar/g' -e '/^$/d' dosya.txt
Birden fazla sed komutu çalıştırmanın iki yolu: -e 'komut1' -e 'komut2' veya 'komut1; komut2'. Daha karmaşık senaryolar için script dosyası: sed -f komutlar.sed dosya.txt.
05 awk — alan modeli ve temel kullanım
awk her satırı alanlara böler ve onlara isimle erişir. "Sütunlarla iş yap" denilince ilk akla gelen araç.
Temel çalışma prensibi
Her satır için:
1. Satırı FS (field separator) ile alanlara böl → $1 $2 $3 … $NF
2. pattern { action } kurallarını sırayla değerlendir
3. Eşleşen action'ları çalıştır
4. Sonraki satıra geç
-F',' veya BEGIN{FS=","}.-F ile sınırlayıcı belirt
# /etc/passwd dosyasından kullanıcı adı ve shell (: sınırlayıcı)
awk -F: '{print $1, $7}' /etc/passwd
# CSV: alan 2 ve 4'ü al
awk -F, '{print $2, $4}' veri.csv
# Bir karakterden uzun sınırlayıcı → regex kullan
awk -F ' ' '{print $1}' # çift boşluk
awk -F '[,;]' '{print $1}' # virgül veya noktalı virgül
print vs printf
# print — otomatik newline ekler, OFS ile alanları ayırır
awk '{print $1, $2}' dosya.txt # aralarında boşluk
awk '{print $1 ":" $2}' dosya.txt # birleştirilmiş string
# printf — C benzeri format kontrolü
awk '{printf "%-20s %5d\n", $1, $3}' dosya.txt
# └── 20 karakter soldan └── 5 karakter sağdan
# İlk 10 satırı al (head ile aynı)
awk 'NR <= 10' dosya.txt
# Son satırı al (tail -1 ile aynı)
awk 'END{print}' dosya.txt
awk -F: '{print $1, $7}' /etc/passwd — her satırı : ile böler, 1. alanı (kullanıcı adı) ve 7. alanı (shell) aralarında boşlukla yazar. root, daemon, kullanıcı adlarının yanında hangi shell'i kullandıklarını tek komutta görürsün.
06 awk — pattern/action, BEGIN/END
awk'un gerçek gücü: koşullu işlem, toplama, ortalama — tek geçişte, dış araç olmadan.
pattern { action } yapısı
# Pattern bir regex: /ERROR/ içeren satırlar
awk '/ERROR/ {print NR, $0}' app.log
# Pattern bir koşul: 3. alan 100'den büyük
awk '$3 > 100 {print $1, $3}' veri.txt
# Pattern yok → tüm satırlara uygula
awk '{toplam += $3} END{print toplam}' veri.txt
# Action yok → eşleşen satırı olduğu gibi yaz (grep gibi)
awk '/^[0-9]/' dosya.txt
BEGIN ve END
# Başlık + içerik + toplam
awk '
BEGIN {
FS = ","
print "KULLANICI", "TUTAR"
}
NR > 1 { # başlık satırını atla
toplam += $3
print $1, $3
}
END {
print "---"
print "TOPLAM:", toplam
}
' satislar.csv
Sayaç ve koşul
# "ERROR" ve "WARN" sayısı
awk '/ERROR/{err++} /WARN/{warn++} END{print "ERROR:", err, "WARN:", warn}' app.log
# Her benzersiz HTTP status kodunun sayısı
awk '{sayac[$9]++} END{for(kod in sayac) print kod, sayac[kod]}' access.log
# Ortalama yanıt süresi (access.log'un son alanı byte, 7. alan status)
awk '{toplam+=$NF; n++} END{printf "Ort: %.2f\n", toplam/n}' access.log
sayac[$9]++ — awk'ta diziler (array) string anahtarlı hash map gibi çalışır, önceden tanımlamana gerek yok. $9 HTTP status kodu için kullanılıyor; her farklı kod için ayrı sayaç tutar. END'de for(anahtar in dizi) ile tüm anahtarları dolaşır.
07 Üçü birlikte — nginx log örneği
Gerçek bir nginx access.log üzerinden grep + sed + awk'u pipeline'da birleştireceğiz.
Örnek log formatı
192.168.1.10 - alice [10/Apr/2026:09:12:33 +0300] "GET /api/users HTTP/1.1" 200 1423 "-" "curl/7.68.0"
10.0.0.5 - bob [10/Apr/2026:09:13:01 +0300] "POST /api/login HTTP/1.1" 401 87 "-" "Mozilla/5.0"
192.168.1.10 - alice [10/Apr/2026:09:13:45 +0300] "GET /api/users/42 HTTP/1.1" 200 891 "-" "curl/7.68.0"
172.16.0.2 - - [10/Apr/2026:09:14:02 +0300] "GET /nonexistent HTTP/1.1" 404 153 "-" "Python/3.9"
10.0.0.5 - bob [10/Apr/2026:09:14:30 +0300] "POST /api/login HTTP/1.1" 200 312 "-" "Mozilla/5.0"
Senaryo 1: Tüm hata yanıtlarını bul
# 4xx veya 5xx satırları + satır numarası
grep -nE '" (4|5)[0-9]{2} ' access.log
Senaryo 2: IP → status kodu tablosu
# awk ile IP ($1) ve status ($9) çek, status'a göre sırala
awk '{print $1, $9}' access.log | sort -k2 -n
Çıktı:
192.168.1.10 200
192.168.1.10 200
10.0.0.5 200
10.0.0.5 401
172.16.0.2 404
Senaryo 3: Her IP'nin kaç istek gönderdiği
awk '{say[$1]++} END{for(ip in say) print say[ip], ip}' access.log | sort -rn
Çıktı:
2 192.168.1.10
2 10.0.0.5
1 172.16.0.2
Senaryo 4: Sadece GET isteklerini filtrele, URL'yi temizle
# grep ile GET filtrele, awk ile URL al, sed ile query string temizle
grep '"GET ' access.log \
| awk '{print $7}' \
| sed 's/?.*//' \
| sort | uniq -c | sort -rn
Açıklama:
grep → sadece GET satırlarını al
awk $7 → 7. alan = URL ("/api/users")
sed → ?query=string kısmını sil
sort|uniq -c|sort -rn → en çok istek alan URL'ler
grep ile ham satır sayısını düşürdün. awk ile yapısal bilgiyi çıkardın. sed ile format temizledi. Her biri kendi işini yaptı, bir sonrakine temiz veri verdi. Bu, Unix felsefesinin özü.
08 Özet — ne zaman hangisi
Doğru aracı seçmek, doğru kullanmak kadar önemlidir.
| Araç | Ne için ideal | Sınırı |
|---|---|---|
| grep | Satır filtrele, var mı yok mu bak, dosya adı bul, bağlam göster | Alan bazlı işlem yapamaz, dönüştüremez, hesaplayamaz |
| sed | Metin ikamesi, satır ekleme/silme, inplace dosya düzenleme | Alan hesaplamak, toplamak, karma veri işlemek güçleşir |
| awk | Yapısal metin: alanları parçala, say, topla, raporla, koşullu işle | Karmaşık iş mantığı için Python daha anlaşılır olabilir |
Hatırlanacaklar
grep -Eextended regex,grep -Fliteral,grep -osadece eşleşen kısımgrep -r --include='*.py'— dosya tipi filtreli özyinelemeli aramased 's/a/b/g'global,'s|/old/path|/new|g'/ içeriyorsa sınırlayıcı değiştirsed -iLinux'ta direkt, macOS'ta-i ''gerekirawk -F, '{print $2}'— alan sınırlayıcı ve alan erişimiawk 'END{print toplam}'— BEGIN/END ile öncesi/sonrası mantığı- Üçlüyü pipeline'da zincirle:
grep | awk | sed | sort | uniq
Bir sonraki adım: find rehberi — dosya sistemi sorguları, -exec, xargs ve prune.
Aynı pipeline araçları için: Unix felsefesi: pipe, tee, xargs.