sed ile Yerinde Düzenleme: Yedekleme Stratejileri ve Güvenli Dosya Güncelleme
Yıllarca üretim ortamında çalışmış biri olarak şunu rahatlıkla söyleyebilirim: sed -i komutu hem en çok sevdiğim hem de en çok korktuğum araçlardan biridir. Doğru kullanıldığında onlarca sunucudaki konfigürasyon dosyasını saniyeler içinde günceller, yanlış kullanıldığında ise kritik bir config’i yerle bir edebilir. Bu yazıda sed‘in yerinde düzenleme özelliğini, buna eşlik eden yedekleme stratejilerini ve gerçek üretim senaryolarında nasıl güvenli kullanılacağını aktaracağım.
sed -i Nedir ve Neden Bu Kadar Güçlüdür?
sed (stream editor) normalde standart çıktıya yazar, dosyayı değiştirmez. -i parametresi ise “in-place” yani dosyanın kendisini değiştirme modunu aktive eder. Bu basit bir fark gibi görünse de pratikte çok büyük bir etki yaratır.
Klasik sed kullanımı:
sed 's/eski/yeni/g' /etc/uygulama/config.conf
Bu komut sonucu terminale basar, dosyaya dokunmaz. Yerinde düzenleme için:
sed -i 's/eski/yeni/g' /etc/uygulama/config.conf
Peki bu arka planda nasıl çalışır? sed -i aslında şunları yapar: geçici bir dosya oluşturur, tüm içeriği değiştirilmiş haliyle oraya yazar, sonra orijinal dosyanın üzerine taşır. Bu mekanizma önemli çünkü symlink’lerle çalışırken bazen beklenmedik davranışlara yol açabilir. Bunu aklınızın bir köşesinde tutun.
GNU sed (Linux’ta standart olan) ve BSD sed (macOS) arasında -i parametresi kullanımında kritik bir fark vardır. Bu fark özellikle hem Linux hem macOS’ta çalışan scriptler yazarken başınızı ağrıtabilir.
GNU sed (Linux):
sed -i 's/foo/bar/g' dosya.txt
sed -i.bak 's/foo/bar/g' dosya.txt # yedek ile
BSD sed (macOS):
sed -i '' 's/foo/bar/g' dosya.txt # boş string zorunlu
sed -i.bak 's/foo/bar/g' dosya.txt # yedek ile
Taşınabilir script yazıyorsanız bu farkı mutlaka gözetmeniz gerekir.
Yerinde Yedekleme: -i Parametresinin Yedek Uzantısı
sed -i‘nin en değerli özelliği, yerinde düzenleme yaparken otomatik yedek oluşturmasıdır. Parametre hemen ardından uzantı alır:
sed -i.bak 's/DEBUG=false/DEBUG=true/g' /etc/myapp/settings.conf
Bu komut çalıştıktan sonra iki dosyanız olur:
/etc/myapp/settings.conf(güncellenmiş)/etc/myapp/settings.conf.bak(orijinal)
Sadece .bak değil, istediğiniz her uzantıyı kullanabilirsiniz:
# Tarih damgalı yedek
sed -i".$(date +%Y%m%d)" 's/old_value/new_value/g' config.cfg
# Versiyon numaralı yedek
sed -i.v1 's/version=1/version=2/g' app.conf
Tarih damgalı yaklaşım özellikle işime yarıyor. Aynı dosyayı birden fazla kez düzenlemeniz gerektiğinde hangi yedeğin ne zaman alındığını net görebiliyorsunuz.
Gerçek Dünya Senaryoları
Senaryo 1: Birden Fazla Sunucuda Nginx Konfigürasyonu Güncelleme
Diyelim ki 20 web sunucunuzda nginx konfigürasyonundaki eski upstream adresini yenisiyle değiştirmeniz gerekiyor. Manuel yaparsanız hem zaman kaybı hem de hata riski yüksek.
#!/bin/bash
SUNUCULAR="web01 web02 web03 web04 web05"
ESKI_IP="192.168.1.100"
YENI_IP="192.168.1.200"
CONF_DOSYA="/etc/nginx/conf.d/upstream.conf"
for sunucu in $SUNUCULAR; do
echo "[$sunucu] Guncelleniyor..."
ssh "$sunucu" "
# Once yedek al
cp ${CONF_DOSYA} ${CONF_DOSYA}.$(date +%Y%m%d_%H%M%S)
# Degisikligi uygula
sed -i 's/${ESKI_IP}/${YENI_IP}/g' ${CONF_DOSYA}
# Nginx syntax kontrolu
nginx -t 2>&1
# Kontrol basarili ise reload et
if nginx -t 2>/dev/null; then
systemctl reload nginx
echo 'Nginx reload basarili'
else
echo 'HATA: Nginx syntax hatasi, degisiklik geri aliniyor'
YEDEK=$(ls -t ${CONF_DOSYA}.* | head -1)
cp $YEDEK ${CONF_DOSYA}
fi
"
done
Bu scriptte dikkat edin: önce manuel yedek alıyoruz, sonra sed -i ile değişiklik yapıyoruz, ardından syntax kontrolü yapıp sorun varsa geri dönüyoruz. Bu akış üretim ortamı için minimum güvenlik standardıdır.
Senaryo 2: Uygulama Konfigürasyonlarında Toplu Şifre/Anahtar Rotasyonu
Bir güvenlik vakası sonrasında veritabanı şifrenizi birden fazla konfigürasyon dosyasında değiştirmeniz gerekiyor. Bu senaryo biraz daha hassas çünkü şifreler genellikle özel karakterler içerir.
#!/bin/bash
ESKI_SIFRE='Eski$ifre123!'
YENI_SIFRE='Yeni$ifre456!'
# Ozel karakterleri sed icin escape etme
escape_sed() {
echo "$1" | sed 's/[[.*^$()+?{|]/\&/g'
}
ESKI_ESCAPED=$(escape_sed "$ESKI_SIFRE")
YENI_ESCAPED=$(escape_sed "$YENI_SIFRE")
# Degistirilecek dosyalari bul
find /etc/uygulama -name "*.conf" -o -name "*.properties" | while read dosya; do
if grep -q "$ESKI_SIFRE" "$dosya"; then
echo "Guncelleniyor: $dosya"
sed -i.sekurite_yedek "s/${ESKI_ESCAPED}/${YENI_ESCAPED}/g" "$dosya"
fi
done
grep -q ile önce dosyanın değiştirilecek içeriği barındırıp barındırmadığını kontrol ediyoruz. Gereksiz yedek oluşturmuyor, gereksiz değişiklik yapmıyoruz. Küçük ama önemli bir pratik.
Senaryo 3: Apache Virtual Host Konfigürasyonu Güncelleme
HTTP’den HTTPS’e geçiş sırasında onlarca virtual host dosyasında ServerName direktiflerini güncellemek zorunda kalmıştım. Şöyle bir yaklaşım kullandım:
# Once ne degistirileceğini gormek icin dry run (degisiklik yapma)
grep -r "ServerName" /etc/apache2/sites-available/ | grep "http://"
# Simdi gercek degisiklik
find /etc/apache2/sites-available/ -name "*.conf" -exec
sed -i.pre_https_migration
's|ServerName http://|ServerName https://|g' {} ;
# Hangi dosyalar degisti kontrol et
find /etc/apache2/sites-available/ -name "*.pre_https_migration" | while read yedek; do
orijinal="${yedek%.pre_https_migration}"
echo "=== $orijinal ==="
diff "$yedek" "$orijinal"
done
Burada iki önemli nokta var. Birincisi find -exec ile sed kombinasyonu, büyük dizin yapılarını dolaşmak için güçlü bir araç. İkincisi, değişiklik sonrası diff ile ne değiştiğini doğrulama adımı. Bu adımı atlamak iş kazalarının başlangıcıdır.
Çoklu Değişiklik: Tek Geçişte Birden Fazla Desen
Bir dosyada birden fazla şey değiştirmeniz gerektiğinde her değişiklik için ayrı sed -i çalıştırmak hem verimsiz hem de her adımda yedek kirliliği yaratır. Doğru yaklaşım tek geçişte halletmek:
# Yanlis: Her degisiklik icin ayri calistirma
sed -i 's/host=localhost/host=db.internal/g' app.conf
sed -i 's/port=5432/port=5433/g' app.conf
sed -i 's/db_name=test/db_name=production/g' app.conf
# Dogru: Tek geciste birden fazla degisiklik
sed -i
-e 's/host=localhost/host=db.internal/g'
-e 's/port=5432/port=5433/g'
-e 's/db_name=test/db_name=production/g'
app.conf
-e parametresi birden fazla expression zinciri oluşturmanıza izin verir. Dosya yalnızca bir kez okunup bir kez yazılır, yedek de sadece bir kez oluşturulur.
Yedek Stratejileri: Sadece .bak Koymak Yetmez
Yerinde düzenleme yapıyorsanız yedekleme konusunda biraz daha sistematik olmanız gerekir. İşte farklı senaryolar için yaklaşımlar:
Versiyon Kontrollü Yedekleme
#!/bin/bash
guvenli_sed() {
local dosya="$1"
shift
local yedek_dizin="/var/backup/config/$(dirname $dosya)"
# Yedek dizini olustur
mkdir -p "$yedek_dizin"
# Mevcut halini versiyonla yedekle
cp "$dosya" "${yedek_dizin}/$(basename $dosya).$(date +%Y%m%d_%H%M%S).$(whoami)"
# sed degisikliklerini uygula
sed -i "$@" "$dosya"
echo "Degisiklik uygulandi: $dosya"
echo "Yedek: ${yedek_dizin}/$(basename $dosya)"
}
# Kullanim
guvenli_sed /etc/myapp/config.ini
-e 's/log_level=DEBUG/log_level=INFO/g'
-e 's/max_connections=10/max_connections=50/g'
Bu fonksiyon ile sed -i komutu etrafına bir güvenlik katmanı sarıyorsunuz. Kim, ne zaman değiştirdi bilgisi .$(whoami) uzantısıyla yedeğe gömülüyor. Ortak kullanılan sunucularda bu bilgi paha biçilmez.
Değişiklik Öncesi Doğrulama Akışı
#!/bin/bash
DOSYA="/etc/postgresql/14/main/postgresql.conf"
PATTERN='max_connections = 100'
YENI='max_connections = 200'
# 1. Dosya var mi?
if [ ! -f "$DOSYA" ]; then
echo "HATA: Dosya bulunamadi: $DOSYA"
exit 1
fi
# 2. Pattern dosyada mevcut mu?
if ! grep -qF "$PATTERN" "$DOSYA"; then
echo "UYARI: Pattern bulunamadi, degisiklik yapilmadi"
echo "Aranan: $PATTERN"
exit 0
fi
# 3. Kac yerde eslesme var? Beklenenden fazla ise dur
ESLESME_SAYISI=$(grep -cF "$PATTERN" "$DOSYA")
if [ "$ESLESME_SAYISI" -gt 1 ]; then
echo "UYARI: Pattern $ESLESME_SAYISI yerde eslesli, beklenen 1"
echo "Devam etmek istiyor musunuz? (e/h)"
read -r cevap
[ "$cevap" != "e" ] && exit 1
fi
# 4. Degisikligi once goster (dry run)
echo "Yapilacak degisiklik:"
sed "s/$PATTERN/$YENI/g" "$DOSYA" | diff "$DOSYA" - | head -20
echo "Onayliyor musunuz? (e/h)"
read -r onay
if [ "$onay" = "e" ]; then
sed -i.$(date +%Y%m%d_%H%M%S) "s/$PATTERN/$YENI/g" "$DOSYA"
echo "Tamamlandi."
fi
Bu script üretim ortamında PostgreSQL parametresi değiştirirken gerçekten kullandığım bir yaklaşıma benziyor. Dry run adımı kritik: değişikliği uygulamadan önce ne olacağını görmek, geri dönülemez hatalardan korur.
Dikkat Edilmesi Gereken Kenar Durumlar
Symlink Sorunu
Daha önce bahsettiğim gibi sed -i arka planda geçici dosya yaratıp rename eder. Bu davranış symlink’leri takip etmez, symlink’i kırar.
# Bu symlink'i kirar
ln -s /etc/uygulama/prod.conf /etc/uygulama/current.conf
sed -i 's/foo/bar/g' /etc/uygulama/current.conf
# Artik current.conf gercek bir dosya, symlink degil
# Symlink korumak icin
sed 's/foo/bar/g' /etc/uygulama/current.conf > /tmp/current.conf.tmp
cat /tmp/current.conf.tmp > /etc/uygulama/current.conf
rm /tmp/current.conf.tmp
cat ile yönlendirme yaklaşımı mevcut inode’u korur, bu da symlink zincirini kırmaz.
Özel Karakterler ve Delimiter Seçimi
URL’ler veya yol ifadeleri içeren pattern’leri değiştirirken / karakteri sorun çıkarır. Delimiter değiştirin:
# Bu calismaz
sed -i 's/http://old.domain.com/http://new.domain.com/g' config.conf
# Farkli delimiter kullanin
sed -i 's|http://old.domain.com|http://new.domain.com|g' config.conf
# Ya da # ile
sed -i 's#/usr/local/old#/usr/local/new#g' script.sh
Büyük Dosyalarda Performans
Yüzlerce megabyte’lık log dosyaları veya büyük veri dosyaları üzerinde sed -i çalıştırırken geçici dosya için yeterli disk alanı olduğundan emin olun. sed -i geçici dosyayı genellikle aynı dizinde oluşturur.
# Disk durumunu kontrol et
df -h $(dirname /path/to/buyuk_dosya)
# Gecici dizini belirterek calistir (GNU sed destekler)
TMPDIR=/dizin/yeterli/alan sed -i 's/eski/yeni/g' buyuk_dosya.txt
Yedekleri Temizleme
Sistematik yedek alıyorsanız temizleme stratejiniz de olmalı. Yedekler birikir ve disk dolar.
#!/bin/bash
# 30 gundan eski config yedeklerini temizle
find /var/backup/config -name "*.bak" -mtime +30 -delete
find /var/backup/config -name "*.2[0-9][0-9][0-9][0-1][0-9][0-3][0-9]*" -mtime +30 -delete
# Belirli dizindeki tum .bak dosyalarini listele
find /etc -name "*.bak" -printf "%st%pn" | sort -n | awk '{print $2}'
# Toplam yer kaplayan yedekler
du -sh /var/backup/config/
Yedek temizliğini de bir cron job’a bağlamak akıllıca bir yaklaşım. Aksi halde bir gün diskten doldu alarmıyla uyanabilirsiniz.
Pratik Notlar ve Son Düşünceler
sed -i ile çalışırken öğrendiğim bazı pratik kurallar şunlar:
- Önce grep ile doğrula: Değişikliği uygulamadan önce pattern’in dosyada olduğunu, doğru sayıda eşleşme verdiğini kontrol et.
- Karmaşık değişiklikler için önce stdout’a bak:
-iolmadan çalıştır, çıktı beklediğin gibiyse-iekle. - Prodüksiyon değişikliklerinde tarih damgalı yedek kullan:
.bakuzantısı aynı dosyayı iki kez değiştirince ilk yedeği ezer, tarih damgası bunu engeller. - Script içinde
sed -ikullanıyorsan hata yönetimi ekle:set -eveya her komut sonrası|| { echo "Hata"; exit 1; }eklemek sizi kurtarır. - macOS ve Linux arasındaki
-ifarkını unutma: Taşınabilir script yazıyorsansed -i.baksözdizimini kullan, her ikisinde de çalışır.
sed konusunda uzmanlaşmak istiyorsanız şunu öneririm: ilk başta -i parametresini mümkün olduğunca az kullanın. Önce standart çıktıya yazarak komutunuzun ne yaptığını anlayın, sonra -i ekleyin. Bu alışkanlık ileride ciddi hatalardan korur.
Yerinde düzenleme güçlü bir araçtır, ama bu güç sorumluluk gerektirir. Üretim ortamında yaptığınız her sed -i işlemi bir değişiklik yönetimi sürecinin parçası olmalıdır: neyi neden değiştirdiğinizi belgeleyin, yedek alın, doğrulayın ve gerekirse geri dönün. Bu adımları atlamak kısa vadede zaman kazandırır gibi görünür, uzun vadede ise sizi gece yarısı incident’larının ortasında bırakır.
Sonuç
sed -i ve yedekleme stratejileri bir teknik konu gibi görünse de aslında özünde operasyonel olgunluk meselesidir. Hangi komutun ne yaptığını bilmek, onu güvenli kullanmaktan farklı bir beceridir. Bu yazıda anlattığım yaklaşımlar yıllar içinde gerçek ortamlarda şekillenmiş pratikler.
Özetle:
sed -i.uzanti: Yerinde düzenleme için otomatik yedek oluşturmanın en temiz yolu-eile çoklu expression: Tek geçişte birden fazla değişiklik için vazgeçilmez- Symlink farkındalığı:
sed -i‘nin symlink davranışını bilin, gerektiğinde alternatif yöntem kullanın - Doğrulama akışı: Dry run, pattern kontrolü ve diff eklemek prodüksiyon güvenliğinin temelidir
- Yedek temizliği: Yedek alıyorsanız temizleme planınız da olsun
Yorumlarınızı ve kendi kullandığınız yaklaşımları paylaşabilirsiniz. Özellikle büyük ölçekli ortamlarda sed -i yönetimi için farklı çözümler keşfetmek hep ilgi çekici oluyor.
