Sistem yöneticiliğinde en sık karşılaştığımız durumlardan biri şudur: Yüzlerce konfigürasyon dosyasında aynı IP adresini değiştirmen gerekiyor, ya da bir proje genelinde eski bir kütüphane adını yenisiyle güncelleme zorundasın. Tek tek dosya açıp değiştirmek saatler alır ve hata yapma ihtimalin çok yüksektir. İşte tam bu noktada sed komutu hayat kurtarıcı olur. Bu yazıda sed‘in çoklu dosyalarda toplu değiştirme yeteneklerini gerçek dünya senaryolarıyla ele alacağız.
sed Nedir ve Neden Bu Kadar Güçlüdür?
sed (Stream Editor), metin akışları üzerinde işlem yapan, Unix dünyasının en köklü araçlarından biridir. Dosyaları satır satır okur, belirlediğin kurallara göre işlem yapar ve çıktıyı üretir. Temel felsefesi “boru hattı” mantığıyla çalışmaktır; yani bir komutun çıktısını alıp işleyebilir, ya da doğrudan dosyalar üzerinde kalıcı değişiklik yapabilirsin.
Çoklu dosyalarda kullanımını bu kadar değerli yapan şey şudur: sed, komut satırında birden fazla dosyayı argüman olarak kabul eder. Buna find, xargs veya glob pattern’lerini eklediğinde, binlerce dosyayı saniyeler içinde işleyebilirsin.
Temel sed sözdizimi şöyledir:
sed [seçenekler] 'komut' dosya1 dosya2 dosya3
En çok kullanacağın seçenekler:
- -i: Dosyayı yerinde (in-place) değiştirir, yani orijinal dosyayı günceller
- -i.bak: Yerinde değiştirirken
.bakuzantılı yedek oluşturur - -e: Birden fazla sed komutu belirtmeni sağlar
- -n: Varsayılan çıktıyı bastırır, sadece
pkomutuyla yazdırılan satırları gösterir - -r veya -E: Genişletilmiş regex (ERE) kullanımını etkinleştirir
- -f: Sed komutlarını bir dosyadan okur
Temel Değiştirme Sözdizimi
sed‘in s komutu (substitute) en sık kullandığın araç olacak. Yapısı şöyle:
s/aranacak_metin/yeni_metin/bayraklar
Bayraklar kısmında kullanabileceğin değerler:
- g: Global, satırdaki tüm eşleşmeleri değiştirir (yoksa sadece ilki)
- i: Büyük/küçük harf duyarsız arama yapar
- p: Değiştirme yapılan satırı yazdırır
- 2, 3 vb.: Satırdaki kaçıncı eşleşmenin değiştirileceğini belirtir
Şimdi gerçek senaryolara geçelim.
Senaryo 1: Sunucu IP Adresi Değişikliği
Diyelim ki altyapında bir veritabanı sunucusunun IP adresi değişti. Eski IP 192.168.1.100, yeni IP 192.168.1.250. Bu bilgi onlarca konfigürasyon dosyasında geçiyor.
# Tek bir dizindeki tüm .conf dosyalarında değişiklik yap
sed -i 's/192.168.1.100/192.168.1.250/g' /etc/myapp/*.conf
Burada dikkat etmen gereken nokta: IP adresindeki noktaları . şeklinde escape etmek zorundasın. Çünkü regex’te . herhangi bir karakteri temsil eder. Escape etmezsen istemediğin eşleşmeler de değişebilir.
Değişiklik yapmadan önce ne değişeceğini görmek istiyorsan:
# Önce sadece kontrol et, dosyaları değiştirme
grep -rn "192.168.1.100" /etc/myapp/
Yedek alarak değiştirme yapmak daha güvenlidir:
# .bak uzantılı yedek oluşturarak değiştir
sed -i.bak 's/192.168.1.100/192.168.1.250/g' /etc/myapp/*.conf
Senaryo 2: find ve xargs ile Alt Dizinlerde Toplu Değiştirme
Tek bir dizinle sınırlı kalmak yerine, alt dizinleri de dahil etmek istediğinde find komutunu devreye sokarsın. Bu kombinasyon özellikle büyük projelerde olmazsa olmazdır.
# Tüm alt dizinlerdeki .conf dosyalarında değiştirme yap
find /etc -name "*.conf" -type f | xargs sed -i 's/eski_deger/yeni_deger/g'
Ancak bu yaklaşımda dikkat etmen gereken bir şey var: Dosya adlarında boşluk varsa xargs hata verebilir. Bunu önlemek için find‘ın -print0 seçeneğini ve xargs‘ın -0 seçeneğini birlikte kullan:
# Dosya adlarında boşluk olma ihtimaline karşı güvenli yöntem
find /etc -name "*.conf" -type f -print0 | xargs -0 sed -i 's/eski_deger/yeni_deger/g'
Bir web projesi örneği verelim. Tüm PHP dosyalarında eski veritabanı host adını değiştirmen gerekiyor:
# PHP dosyalarında db host değişikliği
find /var/www/html -name "*.php" -type f -print0 |
xargs -0 sed -i 's/db.eski-sunucu.com/db.yeni-sunucu.com/g'
Senaryo 3: Birden Fazla Değiştirme İşlemini Tek Seferde Yapmak
Bazen tek bir dosyada ya da dosya grubunda birden fazla farklı değişiklik yapman gerekir. Her değişiklik için ayrı sed çağrısı yapmak yerine, -e seçeneğiyle birden fazla komutu zincirleyebilirsin:
# Aynı anda birden fazla değiştirme işlemi
sed -i
-e 's/eski_host/yeni_host/g'
-e 's/eski_port/yeni_port/g'
-e 's/eski_kullanici/yeni_kullanici/g'
/etc/uygulama/*.conf
Gerçek bir deployment senaryosu düşün. Staging ortamından production’a geçerken birden fazla parametreyi güncellemen gerekiyor:
# Staging'den production'a geçiş parametreleri
find /var/www/uygulama -name "*.env" -o -name "*.cfg" |
xargs sed -i
-e 's/APP_ENV=staging/APP_ENV=production/g'
-e 's/DEBUG=true/DEBUG=false/g'
-e 's/LOG_LEVEL=debug/LOG_LEVEL=error/g'
-e 's/staging.example.com/www.example.com/g'
Senaryo 4: Sed Script Dosyası Kullanmak
Değiştirme işlemleri çok sayıda olduğunda veya bunları tekrar tekrar kullanacaksan, sed komutlarını bir script dosyasına yazıp -f seçeneğiyle çağırabilirsin. Bu yöntem hem okunabilirlik hem de yeniden kullanılabilirlik açısından çok avantajlıdır.
Önce degisiklikler.sed adında bir dosya oluştur:
# degisiklikler.sed dosyası içeriği
s/sunucu1.sirket.com/sunucu1.yeni-sirket.com/g
s/192.168.10./10.0.1./g
s/eski_veritabani/yeni_veritabani/g
s/smtp.eski-mail.com/smtp.yeni-mail.com/g
/^#.*TODO/d
Sonra bu script dosyasını kullan:
# Script dosyasıyla toplu değiştirme
find /etc/uygulama -type f -name "*.conf" |
xargs sed -i -f degisiklikler.sed
Bu yöntemi özellikle büyük altyapı migrasyonlarında kullanıyorum. Tüm değişiklik kurallarını tek bir dosyada toplamak, hem ekip içi iletişimi kolaylaştırıyor hem de git ile versiyon kontrolünü mümkün kılıyor.
Senaryo 5: Regex ile Karmaşık Eşleşmeler
sed‘in gerçek gücü regex desteğinden gelir. -r veya -E seçeneğiyle genişletilmiş regex kullandığında çok daha karmaşık pattern’lerle çalışabilirsin.
Örneğin, tüm log dosyalarındaki eski tarih formatını (DD/MM/YYYY) yeni formata (YYYY-MM-DD) çevirmek istiyorsun:
# Tarih formatı dönüşümü: DD/MM/YYYY -> YYYY-MM-DD
sed -E 's|([0-9]{2})/([0-9]{2})/([0-9]{4})|3-2-1|g' access.log
Burada dikkat et: Sınırlayıcı olarak / yerine | kullandım. Bu özellikle URL veya dosya yolları içeren metinlerde çok işe yarar, çünkü her / karakterini escape etmek zorunda kalmazsın.
Grup yakalama (capturing groups) özelliğini kullanarak daha karmaşık dönüşümler yapabilirsin. 1, 2 gibi geri referanslar yakaladığın grupları kullanmanı sağlar.
Başka bir örnek: Konfigürasyon dosyalarındaki port numaralarını belirli bir aralıkta değiştirmek:
# 8080 ile başlayan tüm port referanslarını 9090 ile değiştir
find /etc -name "*.conf" -type f |
xargs sed -i -E 's/port[[:space:]]*=[[:space:]]*8080/port = 9090/g'
Senaryo 6: Satır Numarasına Göre Hedefleme
sed sadece metin eşleşmesiyle çalışmaz, belirli satır numaralarını veya satır aralıklarını da hedefleyebilirsin. Bu özellik konfigürasyon dosyalarının belirli bölümlerini güncellerken işine yarar.
# Sadece ilk 10 satırda değiştirme yap
sed -i '1,10s/eski/yeni/g' dosya.conf
# Belirli bir kelime içeren satırdan sonraki satırı değiştir
sed -i '/[database]/,/[/{s/host=.*/host=yeni-db-sunucu/}' ayarlar.conf
Toplu dosyalarda belirli bir bölümü güncellemek için:
# Tüm nginx config dosyalarında server_name bloğunu güncelle
find /etc/nginx/sites-available -type f |
xargs sed -i '/server_name/s/eski.domain.com/yeni.domain.com/g'
Senaryo 7: Boş Satır ve Yorum Temizleme
Konfigürasyon dosyalarını temizlerken boş satırları veya yorum satırlarını kaldırmak sık ihtiyaç duyulan bir işlemdir. Özellikle büyük bir altyapıda standartlaştırma yaparken bu çok işe yarar.
# Tüm conf dosyalarından boş satırları kaldır
find /etc/uygulama -name "*.conf" |
xargs sed -i '/^[[:space:]]*$/d'
# Hash ile başlayan yorum satırlarını kaldır
find /etc/uygulama -name "*.conf" |
xargs sed -i '/^[[:space:]]*#/d'
# Hem boş satırları hem de yorumları tek seferde kaldır
find /etc/uygulama -name "*.conf" |
xargs sed -i -e '/^[[:space:]]*$/d' -e '/^[[:space:]]*#/d'
Senaryo 8: Değiştirmeden Önce Yedek Alma Stratejileri
Toplu değişiklik yapmadan önce mutlaka yedek almalısın. Bunun için birkaç farklı yaklaşım var.
Yöntem 1: sed’in -i.bak özelliği
# Her dosyanın yanına .bak uzantılı yedek oluşturur
find /etc/nginx -name "*.conf" |
xargs sed -i.bak 's/worker_processes 1/worker_processes 4/g'
# Değişiklikler yanlış giderse geri almak için:
find /etc/nginx -name "*.conf.bak" |
while read f; do mv "$f" "${f%.bak}"; done
Yöntem 2: Önce test, sonra uygula
# Önce dry-run: neyin değişeceğini gör, dosyaları değiştirme
find /etc -name "*.conf" -type f |
xargs grep -l "eski_deger" |
while read dosya; do
echo "=== $dosya ==="
sed 's/eski_deger/yeni_deger/g' "$dosya" | diff "$dosya" -
done
Bu yaklaşım özellikle kritik sistemlerde çalışırken çok değerlidir. diff çıktısı sana tam olarak neyin değişeceğini gösterir.
Senaryo 9: Büyük Çaplı Proje Migrasyonu
Bir Node.js projesinde require ifadelerini ES6 import södizimine dönüştürmen gerektiğini düşün. Bu, yüzlerce dosyayı etkileyen kapsamlı bir değişikliktir.
# require ifadelerini import'a dönüştür (basit form)
find /proje/src -name "*.js" -type f |
xargs sed -i -E "s/const ([a-zA-Z_]+) = require('([^']+)')/import 1 from '2'/g"
# Daha sonra module.exports'ları da dönüştür
find /proje/src -name "*.js" -type f |
xargs sed -i 's/module.exports = /export default /g'
Ya da bir Python 2’den Python 3’e geçiş senaryosu:
# print ifadelerini fonksiyon södizimine çevir
find /proje -name "*.py" -type f |
xargs sed -i -E 's/^(s*)print ([^(].*)/1print(2)/g'
# raw_input'u input'a çevir
find /proje -name "*.py" -type f |
xargs sed -i 's/raw_input(/input(/g'
Pratik İpuçları ve Yaygın Hatalar
Yıllar içinde edindiğim bazı önemli ipuçlarını paylaşayım:
macOS ve GNU sed farkı: macOS’ta gelen sed BSD versiyonudur ve bazı davranışları GNU sed‘den farklıdır. Özellikle -i seçeneği macOS’ta boş string bile olsa uzantı belirtilmesini zorunlu kılar:
# macOS'ta çalışan versiyon
sed -i '' 's/eski/yeni/g' dosya.txt
# GNU sed (Linux) versiyonu
sed -i 's/eski/yeni/g' dosya.txt
Eğer her iki platformda da çalışacak script yazıyorsan, macOS’a brew install gnu-sed ile GNU sed kurabilirsin.
Özel karakterleri escape etmeyi unutma: URL, dosya yolu veya regex özel karakterleri içeren metinleri değiştirirken dikkatli ol. / . * [ ] ^ $ + ? { } | karakterleri regex’te özel anlam taşır.
# Yanlış: Nokta her karakterle eşleşir
sed -i 's/192.168.1.1/10.0.0.1/g' dosya.conf
# Doğru: Noktalar escape edilmeli
sed -i 's/192.168.1.1/10.0.0.1/g' dosya.conf
Büyük dosyalarda performans: Çok büyük dosyalar veya çok sayıda dosyayla çalışırken xargs‘ın -P seçeneğiyle paralel çalıştırma yapabilirsin:
# 4 paralel süreçle çalıştır
find /var/www -name "*.php" -print0 |
xargs -0 -P 4 sed -i 's/eski_metin/yeni_metin/g'
Değiştirme başarılı oldu mu kontrol et:
# Değiştirme sonrası doğrulama
find /etc -name "*.conf" | xargs grep -l "eski_deger" 2>/dev/null
# Eğer çıktı boşsa değişiklikler başarılı
sed ile grep Kombinasyonu
grep ile önce hangi dosyaların değiştirileceğini belirleyip sonra sed ile sadece o dosyalara işlem uygulamak hem daha hızlı hem de daha güvenlidir:
# Önce grep ile hedef dosyaları bul, sonra sed ile değiştir
grep -rl "eski_string" /etc/uygulama/ | xargs sed -i 's/eski_string/yeni_string/g'
grep -r recursive arama yapar, -l ise sadece eşleşme bulunan dosya adlarını listeler. Bu kombinasyon çok büyük dizin ağaçlarında gereksiz dosyaları işlemekten seni kurtarır.
Sonuç
sed ile çoklu dosyalarda toplu değiştirme, sistem yöneticisinin en güçlü silahlarından biridir. Doğru kullanıldığında saatlik manuel işleri saniyeler içinde halledebilirsin. Önemli olan nokta şu: ne kadar güçlü bir araç olursa olsun, önce test et, yedek al, sonra uygula.
Öğrendiklerimizi özetleyecek olursam: Temel s komutu ve -i seçeneğiyle basit değiştirmeler yapabilirsin. find ve xargs kombinasyonuyla alt dizinlere inebilirsin. -e seçeneği ya da script dosyalarıyla birden fazla değiştirmeyi tek seferde uygulayabilirsin. Regex ve grup yakalama özellikleriyle çok karmaşık dönüşümleri otomatize edebilirsin.
Pratikte en çok işime yarayan kombinasyon şudur: grep -rl ile hedef dosyaları bul, .bak yedek alarak sed -i.bak uygula, diff ile sonuçları doğrula. Bu akışı alışkanlık haline getirdiğinde, toplu dosya değişiklikleri seni artık korkutmaz.