sed ile Düzenli İfadeler: Gelişmiş Desen Eşleştirme ve Yakalama Grupları

Yıllar önce bir log dosyasını elle düzenlemeye çalışırken saatlerimi harcadığımı hatırlıyorum. O gün birisi bana “sed’i düzgün öğrensen beş dakikada biterdi” demişti. Haklıydı. Ama sed’i “düzgün öğrenmek” sandığımdan çok daha derin bir iş çıktı. Özellikle yakalama grupları ve gelişmiş regex kullanımına geçtiğimde, aslında tamamen farklı bir araçla karşı karşıya olduğumu anladım.

Bu yazıda sed’in temel kullanımını geçip, düzenli ifadelerin gerçek gücünü ortaya koyan kısma, yani yakalama grupları, geri referanslar ve gelişmiş desen eşleştirme tekniklerine odaklanacağız. Eğer hala s/foo/bar/g seviyesinde takılıp kaldıysanız, burası tam size göre.

sed’de Regex Modları: BRE ve ERE Farkı

Önce şunu netleştirelim: sed varsayılan olarak BRE (Basic Regular Expressions) kullanır. ERE (Extended Regular Expressions) için -E ya da -r bayrağını kullanmanız gerekir. Bu fark, özellikle yakalama grupları söz konusu olduğunda kritik hale gelir.

BRE’de parantezleri ve artı işaretini escape etmeniz gerekir:

# BRE ile yakalama grubu - ters slash zorunlu
echo "merhaba dünya" | sed 's/(merhaba)/[1]/'
# Çıktı: [merhaba] dünya

# ERE ile aynı işlem - daha temiz
echo "merhaba dünya" | sed -E 's/(merhaba)/[1]/'
# Çıktı: [merhaba] dünya

Günlük kullanımda -E bayrağını tercih etmenizi öneririm. Komutlar çok daha okunabilir oluyor ve hata yapma ihtimaliniz düşüyor. BRE’de escape unutmak, saatlerce debug yapmanıza yol açabilir.

Yakalama Grupları ve Geri Referanslar

Yakalama grupları sed’in en güçlü özelliklerinden biri. Bir deseni yakalayıp sonra ona 1, 2 şeklinde referans verebilirsiniz. Kaç tane grubunuz varsa o kadar referans kullanabilirsiniz, en fazla 9 adet.

Klasik bir örnek: tarih formatı dönüştürme. Türkiye’de çok sık karşılaşılan bir senaryo, farklı sistemlerden gelen log dosyalarında tarih formatlarının karışık olması.

# YYYY-MM-DD formatını DD.MM.YYYY formatına çevir
echo "2024-03-15 sunucu yeniden başlatıldı" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/3.2.1/'
# Çıktı: 15.03.2024 sunucu yeniden başlatıldı

# Toplu log dosyası dönüşümü
sed -i -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/3.2.1/g' access.log

Burada üç ayrı gruba dikkat edin: yıl, ay ve gün. Geri referanslarla sırayı tersine çeviriyoruz. Bu kadar basit ama inanılmaz derecede güçlü.

Gerçek Dünya: Log Dosyası Temizleme

Nginx access log’larından IP adreslerini anonimleştirmek istediğinizi düşünün. GDPR veya KVKK uyumluluğu için bu çok yaygın bir işlem.

# IP adresinin son oktetini maskele
# 192.168.1.105 -> 192.168.1.XXX
sed -E 's/([0-9]{1,3}.){3}[0-9]{1,3}/1XXX/g' access.log

# Daha güvenli versiyon - son okteti sıfırla
sed -E 's/([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}).[0-9]{1,3}/1.0/g' access.log

İkinci örnekte iki farklı yaklaşım görüyorsunuz. Hangisini seçeceğiniz ihtiyacınıza bağlı. Birinci örnekte 1 sadece son tekrarlanan grubu döndürür, bu yüzden biraz yanıltıcı davranış gösterebilir. İkinci örnek daha güvenilir.

Negatif Lookahead Alternatifi: Adres Aralıkları

ERE bile olsa sed’de lookahead yoktur. Ama sed’in adres mekanizmasını kullanarak benzer sonuçlar elde edebilirsiniz.

# Sadece "ERROR" içeren satırlarda dönüşüm yap
sed -n '/ERROR/s/[.*]/[GIZLENDI]/p' uygulama.log

# "DEBUG" içeren satırları atla, diğerlerinde işlem yap
sed '/DEBUG/!s/password=[^ ]*/password=MASKED/g' uygulama.log

! operatörü çok değerli. “Bu desenle eşleşmeyen satırlarda şunu yap” anlamına geliyor. Bunu bilmeden sed kullanmak, bir aracın yarısını kullanmamak gibi.

Çoklu Yakalama Grubu Kullanımı

Apache ya da Nginx konfigürasyon dosyalarında alan adı yeniden adreslemesi yaparken çoklu gruplar çok işe yarıyor:

# Eski domain'i yeni domain ile değiştir ama protokolü ve path'i koru
# http://eski-site.com/sayfa/alt-sayfa -> http://yeni-site.com/sayfa/alt-sayfa
sed -E 's|(https?://)eski-site.com(/[^"]*)?|1yeni-site.com2|g' nginx.conf

# DNS kayıtlarında TTL değerlerini güncelle
# example.com.  3600  IN  A  1.2.3.4 -> example.com.  300  IN  A  1.2.3.4
sed -E 's/(S+s+)3600(s+IN)/13002/g' zone.db

İkinci örnek DNS zone dosyası yönetiminde gerçekten hayat kurtarıyor. TTL değerlerini tek tek aramak yerine tek komutla toplu değişiklik yapabiliyorsunuz.

Çok Satırlı İşlemler: N Komutu

sed satır satır çalışır, ama N komutu ile bir sonraki satırı buffer’a alabilirsiniz. Bu özellik, çok satıra yayılmış bir deseni değiştirmeniz gerektiğinde devreye girer.

# İki satıra yayılmış boş alan temizleme
# "server {" ve hemen altındaki boş satırı birleştir
sed -E '/server {/{N; s/server {n$/server {/}' nginx.conf

# XML/HTML'de birden fazla satıra yayılmış tag'leri bul
# Dikkat: bu basit bir örnek, gerçek XML parsing için xmllint kullanın
sed -n '/<config>/,/</config>/p' ayarlar.xml

N komutuyla ilgili bir uyarı: büyük dosyalarda bellekte iki satır tuttuğu için performans kaybı olmaz, ama script mantığı karmaşıklaşabilir. Bunu gerçekten denemeden önce küçük test dosyaları üzerinde çalışın.

Karakter Sınıfları ve POSIX Sınıfları

sed ERE modunda POSIX karakter sınıfları kullanabilirsiniz. Bu, özellikle Türkçe karakter içeren dosyalarda büyük/küçük harf dönüşümlerinde faydalı olur:

# Sadece rakamlardan oluşan satırları bul ve etiketle
sed -E 's/^([[:digit:]]+)$/SAYI: 1/' veri.txt

# Alfanümerik olmayan karakterleri temizle
sed -E 's/[^[:alnum:][:space:]]//g' metin.txt

# Satır başındaki boşlukları kaldır
sed -E 's/^[[:space:]]+//' girdi.txt

# Hem başındaki hem sonundaki boşlukları kaldır (trim)
sed -E 's/^[[:space:]]+|[[:space:]]+$//' girdi.txt

Son satırdaki | operatörü ERE modunda çalışır ve iki alternatif deseni aynı anda eşleştirmenizi sağlar. BRE modunda bunu iki ayrı sed komutuyla yapmanız gerekirdi.

Yerinde Düzenleme ve Yedekleme

Production’da sed -i kullanırken her zaman yedek alın. Bu konuda çok acı deneyimlerim var:

# .bak uzantısıyla yedek alarak yerinde düzenleme (GNU sed)
sed -i.bak -E 's/MaxConnections=100/MaxConnections=500/g' uygulama.conf

# Birden fazla dosyada toplu değişiklik
find /etc/apache2/sites-enabled/ -name "*.conf" -exec 
  sed -i.bak -E 's/SSLProtocol all/SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1/g' {} ;

# Değişiklik yapılıp yapılmadığını kontrol et
if sed -n '/MaxConnections=500/p' uygulama.conf | grep -q .; then
    echo "Değişiklik başarılı"
    rm uygulama.conf.bak
else
    echo "Değişiklik başarısız, yedekten geri yükleniyor"
    mv uygulama.conf.bak uygulama.conf
fi

Bu script mantığını CI/CD pipeline’larınıza entegre edebilirsiniz. Özellikle konfigürasyon yönetiminde, değişikliğin gerçekten uygulandığını doğrulamak kritik.

Gelişmiş Senaryo: CSV Veri Dönüşümü

Bir müşteri veritabanını farklı bir sisteme aktarırken karşılaştığım gerçek bir senaryo. Kaynak sistem "Ad Soyad","[email protected]","5551234567" formatında veri üretiyordu, hedef sistem ise ad|soyad|email|telefon istiyordu.

# Adım 1: Tırnak işaretlerini kaldır
# Adım 2: Virgülü pipe ile değiştir
# Adım 3: Ad ve soyadı ayır
# Adım 4: Telefon numarasını formatla

sed -E 
  -e 's/"//g' 
  -e 's/,/|/g' 
  -e 's/([A-ZÇĞİÖŞÜa-zçğışöşü]+) ([A-ZÇĞİÖŞÜa-zçğışöşü]+)|/1|2|/' 
  -e 's/|([0-9]{3})([0-9]{3})([0-9]{4})$/|1-2-3/' 
  musteri.csv

-e bayrağı ile birden fazla sed komutu zincirleyebilirsiniz. Bu, karmaşık dönüşümleri adım adım uygulamanın en temiz yolu. Her -e bloğu bir öncekinin çıktısı üzerinde çalışır.

sed Script Dosyaları

Komut satırı uzadıkça okunabilirlik düşer. Karmaşık işlemler için sed script dosyası kullanın:

# donusum.sed dosyası
#!/usr/bin/sed -f

# Yorum satırları # ile başlar

# IP adreslerini maskele
s/([0-9]{1,3}.){3}[0-9]{1,3}/[IP_GIZLENDI]/g

# Email adreslerini maskele
s/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}/[EMAIL_GIZLENDI]/g

# Tarih formatını dönüştür
s/([0-9]{4})-([0-9]{2})-([0-9]{2})/3.2.1/g

# Kullanım:
# sed -E -f donusum.sed kaynak.log > temizlenmis.log

Script dosyalarının bir avantajı daha var: sürüm kontrolüne alabilirsiniz. Ekip içinde paylaşılan sed script’leri, standart log işleme prosedürlerinin tutarlı uygulanmasını sağlar.

Performans: sed mi, awk mi, Python mi?

Büyük dosyalarda sed hızlı ama sınırlı. Kararı nasıl verirsiniz?

  • sed tercih edin: Satır bazlı basit yer değiştirme işlemlerinde, tek geçişli dönüşümlerde, pipeline’ın bir parçası olarak kullanıldığında
  • awk’a geçin: Sütun bazlı işlemlerde, hesaplama gerektiren durumlarda, koşullu mantık karmaşıklaştığında
  • Python/Perl kullanın: Çok satırlı karmaşık desenler, gerçek lookahead/lookbehind gerektiğinde, veri yapıları gerektiğinde
# Bu işi sed yapar ama yavaş olur - 10GB log için awk daha iyi
# Sadece belirli saat aralığındaki satırları al
sed -n '/[10:[0-9]{2}:[0-9]{2}]/p' büyük.log

# Aynısı awk ile daha verimli
awk '/[10:[0-9]{2}:[0-9]{2}]/' büyük.log

Bu karşılaştırmayı yapmak önemli çünkü sed’i her şeye uygun bir araç olarak görmek, gereksiz karmaşıklığa yol açar. Her aracın güçlü olduğu bir alan var.

Sık Yapılan Hatalar ve Çözümleri

Deneyimlerime göre en sık yapılan hatalar şunlar:

BRE/ERE karışıklığı: -E kullanmadan +, ?, |, () karakterlerini escape etmeden kullanmak.

# YANLIŞ (BRE modunda)
echo "test123" | sed 's/[0-9]+/SAYI/'

# DOĞRU (BRE modunda)
echo "test123" | sed 's/[0-9]+/SAYI/'

# DOĞRU (-E ile ERE modunda)
echo "test123" | sed -E 's/[0-9]+/SAYI/'

Açgözlü eşleştirme problemi: sed regex’leri varsayılan olarak açgözlüdür.

# Sorunlu: İlk ve son tırnak arasındaki her şeyi alır
echo '"değer1" ve "değer2"' | sed -E 's/".*"/DEGISTIRILDI/'
# Çıktı: DEGISTIRILDI (her iki tırnağı da kapsar)

# Düzeltilmiş: Tırnak içinde tırnak olmayan karakterleri eşleştir
echo '"değer1" ve "değer2"' | sed -E 's/"[^"]*"/DEGISTIRILDI/g'
# Çıktı: DEGISTIRILDI ve DEGISTIRILDI

macOS ile GNU sed uyumsuzluğu: macOS’taki sed BSD kökenlidir, davranışı farklıdır.

# macOS'ta -i kullanımı (boş string gerekli)
sed -i '' -E 's/eski/yeni/g' dosya.txt

# GNU sed (Linux)
sed -i -E 's/eski/yeni/g' dosya.txt

# Taşınabilir çözüm: macOS'a GNU sed kur
brew install gnu-sed
# Sonra gsed kullan

Sonuç

sed ile düzenli ifadeler, bir sistem yöneticisinin en güçlü silahlarından biri. Ama bu güç, temel s/eski/yeni/g kalıplarını ezberlemekten değil, yakalama gruplarını, adres mekanizmasını ve BRE/ERE farkını gerçekten anlamaktan geliyor.

Bugün ele aldığımız konuları özetlemek gerekirse: -E bayrağıyla ERE modunu tercih edin, yakalama gruplarını 19 referanslarıyla kullanın, ! operatörüyle negatif eşleştirme yapın, karmaşık dönüşümleri -e zincirleri veya script dosyalarıyla yönetin. Ve en önemlisi, sed -i öncesinde daima yedek alın.

Gerçek ustalık ise şurada: bir göreve başlamadan önce “Bu iş için sed mi, awk mi, Python mu?” sorusunu sormak. sed’i ne zaman kullanacağınızı bilmek kadar, ne zaman kullanmayacağınızı bilmek de önemli. Ama doğru kullanıldığında, sed ile birkaç satırda yapabileceğiniz şeyler, sizi ofiste legend yapabilir.

Bir yanıt yazın

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