Arşivleme İşlemlerinde Karakter Kodlaması Sorunlarını Çözme: UTF-8 ve Özel Karakterler İçeren Dosya Adlarını Güvenle Yedekleme

Yıllar önce bir müşterimizin prodüksiyon sunucusunda yedekleme scripti çalıştırdım. Sabah uyandım, her şey yolunda görünüyordu. Ama birkaç hafta sonra gerçek bir felaketle karşılaşıldığında ve yedeği geri yüklemek gerektiğinde, “Müşteri Belgeleri”, “Özel Şablonlar” gibi klasörlerin ya hiç yedeklenmediğini ya da içlerinin boş göründüğünü fark ettik. Sorun basitti: tar dosya adlarındaki Türkçe karakterleri yutmuştu. O günden bu yana karakter kodlaması meselesine çok daha ciddiye alıyorum.

Sorunun Kökeni: Locale ve Encoding Meselesi

Linux sistemlerde dosya adları aslında sadece byte dizileridir. Kernel, dosya adının ne anlama geldiğiyle ilgilenmez. Ama tar, zip, rsync gibi araçlar bu byte dizilerini işlerken kullandıkları locale ayarlarına göre farklı davranır. Sistem locale’i LANG=C veya LANG=POSIX olarak ayarlanmışsa, bu araçlar yalnızca ASCII karakterleri “anlıyor” ve multibyte karakter dizileri karşısında ya hata veriyor ya da sessizce atlıyor.

Türkçe karakter problemi özellikle şu senaryolarda baş gösterir:

  • Kullanıcıların kendi bilgisayarlarından FTP/SFTP ile yüklediği dosyalar (“ürün_listesi_güncellendi.xlsx” gibi)
  • Windows’tan Samba üzerinden kopyalanan dosyalar
  • Farklı encoding’lerle oluşturulmuş eski arşivler
  • Farklı locale ayarlarına sahip sistemler arasında taşınan dizinler

Önce mevcut durumu anlamak gerekir:

# Mevcut locale ayarlarını kontrol et
locale

# Sistemdeki mevcut dosya adlarının encoding'ini tespit et
find /var/data -name "*" | file -i -

# Hangi karakterlerin sorun çıkardığını görmek için
ls -la /var/data | cat -v

cat -v çıktısında ^? veya M- ile başlayan karakter dizileri görüyorsanız, orada non-ASCII karakterler var demektir.

tar Komutu ile Güvenli Arşivleme

tar en yaygın kullanılan arşivleme aracıdır ve doğru parametrelerle UTF-8 dosya adlarını mükemmel şekilde yönetir.

# Temel güvenli arşivleme - locale açıkça belirt
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 tar -czvf yedek_$(date +%Y%m%d).tar.gz /var/data/

# --warning=no-file-changed ile aktif yazılan dosyalardaki uyarıları bastır
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 tar 
  --warning=no-file-changed 
  -czvf /backup/yedek_$(date +%Y%m%d_%H%M).tar.gz 
  /var/data/belgeler/

# Dosya adlarını verbose olarak listele ve log'a yaz
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 tar 
  -czvf /backup/arsiv.tar.gz 
  /var/data/ 2>&1 | tee /var/log/yedek_$(date +%Y%m%d).log

Burada kritik nokta şu: LANG ve LC_ALL değişkenlerini komutun önüne koymak, yalnızca o komut için geçerli ortam değişkeni ataması yapar. Sistem genelini etkilemez, bu da güvenli bir yöntemdir.

tar ile Özel Karakter İçeren Dosyaları Listeden Yedekleme

Bazen yüzlerce dizin içinden sadece belirli dosyaları yedeklemek gerekir. Bu durumda --files-from parametresi hayat kurtarır:

# Önce yedeklenecek dosyaları bir listeye al
find /var/data -name "*.pdf" -o -name "*.docx" | 
  LANG=tr_TR.UTF-8 sort > /tmp/yedeklenecekler.txt

# Listeden oku ve arşivle
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 tar 
  --files-from=/tmp/yedeklenecekler.txt 
  -czvf /backup/belgeler_$(date +%Y%m%d).tar.gz

# Arşiv içeriğini doğrula
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 tar -tzvf /backup/belgeler_$(date +%Y%m%d).tar.gz | head -50

–files-from: Arşivlenecek dosyaların listesini bir dosyadan okur

–warning=no-file-changed: Arşivleme sırasında değişen dosyalar için uyarı vermez

-t: Arşiv içeriğini listeler, çıkarmaz

zip ve unzip ile UTF-8 Desteği

zip aracı biraz daha karmaşık bir hikayedir. PKZIP standardının eski versiyonları UTF-8’i desteklemez, bu yüzden özellikle belirtmek gerekir:

# -UN=UTF8 ile UTF-8 dosya adlarını zorla kullan
zip -r -UN=UTF8 /backup/arsiv.zip /var/data/belgeler/

# Türkçe karakter içeren dosyaları zip'e alırken
LANG=tr_TR.UTF-8 zip -r 
  /backup/proje_$(date +%Y%m%d).zip 
  /var/data/proje/ 
  -x "*.tmp" -x "*~"

# Zip içeriğini UTF-8 ile listele
unzip -l -O UTF-8 /backup/arsiv.zip | head -30

# UTF-8 encoding ile çıkar
unzip -O UTF-8 /backup/arsiv.zip -d /tmp/geri_yukleme/

-UN=UTF8: Unicode dosya adı encoding’ini UTF-8 olarak zorlar

-O UTF-8: unzip’te output encoding’ini belirler

-x: Belirtilen pattern’a uyan dosyaları dışarıda bırakır

Windows sistemlerde oluşturulmuş zip dosyalarını açarken özellikle dikkat edin. Windows’un varsayılan encoding’i CP1254 (Türkçe Windows) olduğu için:

# Windows'tan gelen Türkçe zip dosyalarını açmak için
unzip -O CP1254 windows_backup.zip -d /tmp/acilmis/

# Ya da convmv ile dosya adlarını dönüştür
# Önce convmv'yi kur: apt install convmv / yum install convmv
convmv -f cp1254 -t utf-8 --notest /tmp/acilmis/

rsync ile Encoding Güvenli Senkronizasyon

rsync de karakter kodlaması sorunlarından nasibini alır, özellikle farklı sistemler arasında senkronizasyon yapıldığında:

# Temel UTF-8 güvenli rsync
LANG=tr_TR.UTF-8 rsync -avz 
  --progress 
  /var/data/belgeler/ 
  kullanici@backup-sunucu:/backup/belgeler/

# İkonize edilmiş dosya adları dahil tam yedekleme
LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 rsync 
  -avz 
  --delete 
  --exclude="*.tmp" 
  --exclude=".DS_Store" 
  --log-file=/var/log/rsync_$(date +%Y%m%d).log 
  /var/data/ 
  backup-sunucu:/backup/data/

# Rsync ile checksum doğrulaması
LANG=tr_TR.UTF-8 rsync 
  -avzc 
  --stats 
  /var/data/kritik_belgeler/ 
  /backup/kritik/

-a: Archive modu, sembolik linkler, izinler, zaman damgaları korunur

-v: Verbose, işlem detaylarını gösterir

-z: Aktarım sırasında sıkıştırma kullanır

–delete: Kaynakta silinmiş dosyaları hedeften de siler

-c: Boyut/zaman yerine checksum ile karşılaştırır

Gerçek Dünya Senaryosu: Karma Encoding’li Dizini Temizleme

Çalıştığım ortamlarda en sık karşılaştığım tablo şu: Eski sistemden taşınmış, bir kısmı ISO-8859-9, bir kısmı UTF-8, hatta bir kısmı CP1254 encoding’iyle dosya adı içeren bir dizin. Bu kaosa birkaç adımda çözüm bulunur:

#!/bin/bash
# encoding_denetim.sh - Dosya adı encoding sorunlarını tespit et

HEDEF_DIZIN="/var/data/eski_sistem"
LOG_DOSYASI="/tmp/encoding_rapor_$(date +%Y%m%d_%H%M%S).log"

echo "=== Encoding Denetim Raporu ===" > "$LOG_DOSYASI"
echo "Tarih: $(date)" >> "$LOG_DOSYASI"
echo "Dizin: $HEDEF_DIZIN" >> "$LOG_DOSYASI"
echo "" >> "$LOG_DOSYASI"

# ASCII olmayan dosya adlarını bul
echo "--- ASCII Olmayan Dosya Adları ---" >> "$LOG_DOSYASI"
find "$HEDEF_DIZIN" -name "*" | 
  grep -P '[^x00-x7F]' >> "$LOG_DOSYASI" 2>&1

# Geçersiz UTF-8 byte sequence içeren dosya adları
echo "" >> "$LOG_DOSYASI"
echo "--- Geçersiz UTF-8 İçerenler ---" >> "$LOG_DOSYASI"
find "$HEDEF_DIZIN" -name "*" | 
  iconv -f UTF-8 -t UTF-8 2>&1 | 
  grep "EILSEQ|invalid" >> "$LOG_DOSYASI"

echo "Rapor hazır: $LOG_DOSYASI"
cat "$LOG_DOSYASI"

Bu scripti çalıştırdıktan sonra sorunlu dosyaları convmv ile düzeltiriz:

# Önce dry-run ile test et, hiçbir şeyi değiştirme
convmv -f iso-8859-9 -t utf-8 -r /var/data/eski_sistem/

# Sonuçtan memnunsan gerçekten uygula
convmv -f iso-8859-9 -t utf-8 --notest -r /var/data/eski_sistem/

# Dönüşüm sonrası doğrulama
find /var/data/eski_sistem -name "*" | 
  LANG=tr_TR.UTF-8 sort | head -50

Otomatik Yedekleme Scripti: Production Ready

Yukarıdaki tüm bilgileri bir araya getiren, gerçekten production ortamında kullanılabilecek bir yedekleme scripti:

#!/bin/bash
# utf8_yedek.sh - UTF-8 güvenli arşivleme scripti
# Versiyon: 2.1
# Kullanim: ./utf8_yedek.sh [kaynak_dizin] [hedef_dizin]

set -euo pipefail

# Değişkenler
KAYNAK="${1:-/var/data}"
HEDEF="${2:-/backup}"
TARIH=$(date +%Y%m%d_%H%M%S)
ARSIV_ADI="yedek_${TARIH}.tar.gz"
LOG_DOSYASI="/var/log/yedek_${TARIH}.log"
ENCODING="tr_TR.UTF-8"

# Renkli çıktı
KIRMIZI='33[0;31m'
YESIL='33[0;32m'
SARI='33[1;33m'
NC='33[0m'

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DOSYASI"
}

hata() {
    echo -e "${KIRMIZI}[HATA] $1${NC}" | tee -a "$LOG_DOSYASI"
    exit 1
}

# Ön kontroller
[ -d "$KAYNAK" ] || hata "Kaynak dizin bulunamadı: $KAYNAK"
[ -d "$HEDEF" ] || mkdir -p "$HEDEF"

# Locale kontrolü ve ayarı
if ! locale -a | grep -q "tr_TR.utf8"; then
    log "${SARI}UYARI: tr_TR.UTF-8 locale bulunamadı, en_US.UTF-8 kullanılıyor${NC}"
    ENCODING="en_US.UTF-8"
fi

export LANG="$ENCODING"
export LC_ALL="$ENCODING"

log "Yedekleme başlıyor: $KAYNAK -> $HEDEF/$ARSIV_ADI"
log "Kullanılan encoding: $ENCODING"

# Kaynak dizindeki encoding sorunlarını tespit et
SORUNLU_DOSYA_SAYISI=$(find "$KAYNAK" -name "*" 2>/dev/null | 
    iconv -f UTF-8 -t UTF-8 2>&1 | 
    grep -c "illegal|invalid" || true)

if [ "$SORUNLU_DOSYA_SAYISI" -gt 0 ]; then
    log "${SARI}UYARI: $SORUNLU_DOSYA_SAYISI adet encoding sorunu olan dosya tespit edildi${NC}"
fi

# Arşivleme
BASLANGIC=$(date +%s)

tar 
    --warning=no-file-changed 
    --warning=no-file-removed 
    -czf "${HEDEF}/${ARSIV_ADI}" 
    -C "$(dirname "$KAYNAK")" 
    "$(basename "$KAYNAK")" 
    2>> "$LOG_DOSYASI"

BITIS=$(date +%s)
SURE=$((BITIS - BASLANGIC))

# Arşiv boyutu ve içerik doğrulaması
ARSIV_BOYUTU=$(du -sh "${HEDEF}/${ARSIV_ADI}" | cut -f1)
DOSYA_SAYISI=$(tar -tzf "${HEDEF}/${ARSIV_ADI}" | wc -l)

log "Arşivleme tamamlandı."
log "Boyut: $ARSIV_BOYUTU | Dosya sayısı: $DOSYA_SAYISI | Süre: ${SURE}s"

# Bütünlük kontrolü
if tar -tzf "${HEDEF}/${ARSIV_ADI}" > /dev/null 2>&1; then
    log "${YESIL}Bütünlük kontrolü başarılı${NC}"
else
    hata "Arşiv bütünlük kontrolü başarısız! Dosya bozuk olabilir."
fi

# Eski yedekleri temizle (30 günden eski)
find "$HEDEF" -name "yedek_*.tar.gz" -mtime +30 -delete
log "30 günden eski yedekler temizlendi."

echo -e "${YESIL}Yedekleme başarıyla tamamlandı: ${HEDEF}/${ARSIV_ADI}${NC}"

Arşivden Geri Yükleme: Encoding’i Koruma

Yedek almak kadar önemli olan geri yükleme sürecinde de encoding dikkat gerektirir:

# Arşiv içeriğini önce listele ve encoding'i kontrol et
LANG=tr_TR.UTF-8 tar -tzvf /backup/yedek_20241215.tar.gz | 
  grep -v "/$" | head -20

# Belirli bir dizini geri yükle
LANG=tr_TR.UTF-8 tar 
  -xzvf /backup/yedek_20241215.tar.gz 
  -C /tmp/geri_yukleme/ 
  --strip-components=2 
  var/data/belgeler/

# Geri yükleme sonrası dosya adlarını doğrula
find /tmp/geri_yukleme/ -name "*" | LANG=tr_TR.UTF-8 sort

# İzinleri de orijinaliyle karşılaştır
LANG=tr_TR.UTF-8 tar 
  --same-owner 
  --preserve-permissions 
  -xzvf /backup/yedek_20241215.tar.gz 
  -C /restore/

–strip-components: Arşivdeki dizin katmanlarını soyar, belirtilen sayıda üst dizini atar

–same-owner: Dosya sahipliğini arşivdeki gibi korur

–preserve-permissions: Dosya izinlerini korur

Cron ile Düzenli Yedekleme Planı

Script hazır, şimdi cron’a alalım. Burada da encoding dikkat gerektirir çünkü cron’un kendisi minimal bir environment ile çalışır:

# crontab -e ile aç ve şunu ekle:

# Her gece 02:00'da UTF-8 güvenli yedekleme
0 2 * * * LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 /usr/local/bin/utf8_yedek.sh /var/data /backup >> /var/log/cron_yedek.log 2>&1

# Haftasonları tam yedek, hafta içi artımlı
0 3 * * 6,0 LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 /usr/local/bin/tam_yedek.sh >> /var/log/cron_tam_yedek.log 2>&1
0 3 * * 1-5 LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 /usr/local/bin/artimli_yedek.sh >> /var/log/cron_artimli.log 2>&1

Cron job’larında sık yapılan hata, script içinde locale ayarlamak yerine environment’ı cron’un kendisine bırakmaktır. Yukarıdaki gibi LANG ve LC_ALL‘ı cron satırında açıkça vermek, bu problemi tamamen ortadan kaldırır.

Sistemin Genelini Doğru Ayarlamak

Tüm bu tek seferlik çözümler yerine, sistemi baştan doğru yapılandırmak en kalıcı yoldur:

# Mevcut locale listesini gör
locale -a

# Türkçe UTF-8 locale'i oluştur (Debian/Ubuntu)
sudo locale-gen tr_TR.UTF-8
sudo update-locale LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8

# Sonra /etc/environment dosyasına ekle
echo 'LANG=tr_TR.UTF-8' | sudo tee -a /etc/environment
echo 'LC_ALL=tr_TR.UTF-8' | sudo tee -a /etc/environment

# Sistemi yeniden başlatmadan geçerli oturum için uygula
source /etc/environment
locale

RedHat/CentOS tabanlı sistemlerde:

# Locale ayarlarını düzenle
sudo localectl set-locale LANG=tr_TR.UTF-8

# Doğrula
localectl status

Sonuç

Karakter kodlaması sorunları, “sistem çalışıyor” iken fark edilmeyen ama tam da en kritik anda, yani geri yükleme anında yüzünüze çarpan türden sorunlardır. Bu yüzden yedek scriptlerini yazdıktan sonra en önemli adım gerçek bir geri yükleme testi yapmaktır. Türkçe karakter içeren bir klasör ve dosya oluşturun, arşivleyin, farklı bir konuma çıkarın ve dosya adlarının düzgün geldiğini gözlerinizle görün.

Özet olarak hatırlanması gerekenler şunlar:

  • tar ve zip kullanırken her zaman LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8 önekini koy
  • Windows’tan gelen zip dosyalarını unzip -O CP1254 ile aç
  • convmv ile toplu encoding dönüşümü yap, ama her zaman önce dry-run dene
  • Cron job’larında locale’i mutlaka cron satırında belirt, scripte güvenme
  • Yedekleme scriptine bütünlük kontrolü ekle, kör yedek alma
  • Düzenli olarak gerçek geri yükleme testi yap

Bir yedekleme sistemi, yalnızca başarıyla geri yüklenebildiğinde gerçek bir yedekleme sistemidir. Geri kalanı sadece disk doldurmaktır.

Bir yanıt yazın

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