PowerShell ile Toplu Dosya Yeniden Adlandırma

Yüzlerce dosyayı tek tek yeniden adlandırmak zorunda kaldığınız bir anı düşünün. Belki bir fotoğraf arşivini düzenliyorsunuzdur, belki sunucudaki log dosyalarına tarih eklemek istiyorsunuzdur ya da bir uygulama geçişi sırasında binlerce dosyanın uzantısını değiştirmeniz gerekiyordur. Windows Explorer’da bunu yapmaya çalışmak, hem zaman kaybı hem de hata riski demektir. İşte tam bu noktada PowerShell devreye giriyor ve hayatınızı kurtarıyor.

PowerShell’in dosya yönetimi konusundaki gücü gerçekten etkileyici. Rename-Item cmdlet’i ve pipeline kullanımıyla birleştiğinde, saatlik işleri dakikalara, hatta saniyelere indirebilirsiniz. Bu yazıda, gerçek dünya senaryolarından yola çıkarak PowerShell ile toplu dosya yeniden adlandırma konusunu derinlemesine ele alacağız.

Temel Komutlar ve Kavramlar

PowerShell’de dosya yeniden adlandırma işleminin temel taşı Rename-Item cmdlet’idir. Ama çoğu zaman bunu Get-ChildItem ile birlikte kullanırsınız. Bu iki cmdlet’in birlikte çalışması, neredeyse sonsuz esneklik sağlar.

# Tek bir dosyayı yeniden adlandırma
Rename-Item -Path "C:logsaccess.log" -NewName "access_backup.log"

# Get-ChildItem ile birlikte kullanım
Get-ChildItem -Path "C:logs" -Filter "*.txt" | Rename-Item -NewName { $_.Name -replace "old", "new" }

Burada dikkat etmeniz gereken önemli bir nokta var: Rename-Item cmdlet’i, NewName parametresinde bir script block (süslü parantez içindeki kod bloğu) kabul eder. Bu script block içinde $_ değişkeni, pipeline’dan gelen mevcut dosyayı temsil eder. Bu özellik sayesinde her dosya için dinamik isimler üretebilirsiniz.

WhatIf Parametresi: En İyi Dostunuz

Toplu işlemlerde en büyük tehlike, yanlış bir regex veya yanlış bir path yüzünden beklenmedik sonuçlar almaktır. WhatIf parametresi, komutu gerçekten çalıştırmadan önce ne yapacağını gösterir.

# WhatIf ile güvenli test
Get-ChildItem -Path "C:data*.csv" | Rename-Item -NewName { $_.Name -replace "2023", "2024" } -WhatIf

Bu komut çalıştığında ekranda şunu görürsünüz: What if: Performing the operation "Rename File" on target... Bu çıktıyı inceleyip her şey doğruysa -WhatIf parametresini kaldırarak gerçek işlemi yapabilirsiniz. Bu alışkanlığı edinmek, birçok felaketten sizi koruyacaktır. Ben bu dersi zor öğrendim, siz öğrenmeden öğrenin.

Senaryo 1: Uzantı Değiştirme

Bir uygulama geçişi sırasında sıkça karşılaşılan durum, yüzlerce dosyanın uzantısını değiştirmek zorunda kalmaktır. Diyelim ki eski bir sistemden .dat uzantılı dosyalar geldi ve bunları .csv yapmanız gerekiyor.

# Tüm .dat dosyalarını .csv olarak yeniden adlandırma
Get-ChildItem -Path "C:importdata" -Filter "*.dat" | 
    Rename-Item -NewName { [System.IO.Path]::ChangeExtension($_.Name, ".csv") }

# Alt dizinleri de dahil etmek için -Recurse kullanın
Get-ChildItem -Path "C:importdata" -Filter "*.dat" -Recurse | 
    Rename-Item -NewName { [System.IO.Path]::ChangeExtension($_.Name, ".csv") }

[System.IO.Path]::ChangeExtension() metodunu kullanmak, basit string replace yapmaktan çok daha güvenlidir. Çünkü bu metod dosya adındaki noktaları doğru şekilde işler. Örneğin rapor.2024.dat gibi bir dosya adında basit replace yaparsanız beklenmedik sonuçlar alabilirsiniz.

Senaryo 2: Tarih ve Zaman Damgası Ekleme

Log yönetiminde sıkça yapılan bir işlem, arşivlenen dosyalara tarih eklemektir. Özellikle otomatik arşivleme scriptlerinde bu işlemi sık sık göreceksiniz.

# Dosya adının başına bugünün tarihini ekleme
$tarih = Get-Date -Format "yyyyMMdd"
Get-ChildItem -Path "C:logsarchive" -Filter "*.log" | 
    Rename-Item -NewName { $tarih + "_" + $_.Name }

# Dosyanın kendi LastWriteTime değerini kullanma
Get-ChildItem -Path "C:logsarchive" -Filter "*.log" | 
    Rename-Item -NewName { 
        $dosyaTarihi = $_.LastWriteTime.ToString("yyyyMMdd_HHmmss")
        $dosyaTarihi + "_" + $_.BaseName + $_.Extension
    }

İkinci örnekte dikkat edin: Script block içinde birden fazla satır kullanabilirsiniz. $_.BaseName dosyanın uzantısız adını, $_.Extension ise uzantısını verir. Bu ikisini ayrı ayrı manipüle etmek, daha temiz sonuçlar almanızı sağlar.

Senaryo 3: Düzenli İfadeler (Regex) ile Gelişmiş Yeniden Adlandırma

Regex, PowerShell’de dosya adlandırmanın gerçek gücünü ortaya çıkarır. Karmaşık desenleri yakalamak ve değiştirmek için vazgeçilmezdir.

# Dosya adındaki boşlukları alt çizgiyle değiştirme
Get-ChildItem -Path "C:documents" -Filter "*.docx" | 
    Rename-Item -NewName { $_.Name -replace 's+', '_' }

# Birden fazla replace işlemi zincirleyerek yapma
Get-ChildItem -Path "C:documents" -Filter "*.docx" | 
    Rename-Item -NewName { 
        $_.Name -replace 's+', '_' `
                -replace '[öÖ]', 'o' `
                -replace '[üÜ]', 'u' `
                -replace '[çÇ]', 'c' `
                -replace '[şŞ]', 's' `
                -replace '[ğĞ]', 'g' `
                -replace '[ıİ]', 'i'
    }

İkinci örnek özellikle web sunucularına dosya yüklemeden önce çok işe yarar. Türkçe karakterli dosya adları, birçok sistem ve web sunucusunda sorun yaratır. Bu script, Türkçe karakterleri ASCII karşılıklarıyla değiştirir.

Regex ile Desen Yakalama ve Yeniden Düzenleme

Bazen dosya adındaki bilgileri yeniden düzenlemek gerekir. Örneğin, rapor_2024_01_15.xlsx formatındaki dosyaları 2024-01-15_rapor.xlsx formatına çevirmek isteyebilirsiniz.

# Tarih formatını ve konumunu değiştirme
Get-ChildItem -Path "C:raporlar" -Filter "*.xlsx" | 
    Rename-Item -NewName {
        if ($_.BaseName -match '^(.+)_(d{4})_(d{2})_(d{2})$') {
            $dosyaAdi = $matches[1]
            $yil = $matches[2]
            $ay = $matches[3]
            $gun = $matches[4]
            "$yil-$ay-$gun`_$dosyaAdi$($_.Extension)"
        } else {
            $_.Name  # Desene uymuyorsa değiştirme
        }
    }

Bu örnekte if-else yapısı çok önemli bir role sahip. Eğer dosya adı beklenen desene uymuyorsa, script dosyayı olduğu gibi bırakıyor. Bu tür güvenlik kontrolleri olmadan toplu işlemler tehlikeli olabilir.

Senaryo 4: Sıralı Numaralandırma

Fotoğraf arşivleri veya toplu içe aktarma işlemlerinde dosyaları sıralı numaralarla adlandırmak gerekebilir. Bu işlem biraz daha dikkat ister çünkü Rename-Item pipeline’ında sıralama garantisi tam değildir.

# Dosyaları tarihe göre sıralayıp numaralandırma
$sayac = 1
Get-ChildItem -Path "C:fotolar" -Filter "*.jpg" | 
    Sort-Object -Property LastWriteTime | 
    ForEach-Object {
        $yeniAd = "foto_{0:D4}{1}" -f $sayac, $_.Extension
        Rename-Item -Path $_.FullName -NewName $yeniAd
        $sayac++
    }

Burada {0:D4} formatı, sayacı 4 basamaklı sıfır dolgulu formatta gösterir: 0001, 0002, 0003 gibi. Bu sayede alfabetik sıralama ile sayısal sıralama aynı sonucu verir.

Dikkat etmeniz gereken bir husus var: Eğer hedef klasörde hem foto_0001.jpg gibi mevcut dosyalar varsa, yeniden adlandırma sırasında çakışma yaşanabilir. Bu durumda önce dosyaları geçici isimlerle adlandırıp sonra final isimlerini vermek gerekebilir.

Senaryo 5: Prefix ve Suffix Ekleme/Kaldırma

Bir departman değişikliği veya proje geçişi sırasında dosyaların başına veya sonuna belirli etiketler eklemeniz gerekebilir.

# Tüm dosyaların başına prefix ekleme
Get-ChildItem -Path "C:projelerbeta" | 
    Where-Object { -not $_.PSIsContainer } |
    Rename-Item -NewName { "BETA_" + $_.Name }

# Belirli bir prefix'i kaldırma
Get-ChildItem -Path "C:projelerbeta" -Filter "BETA_*" | 
    Rename-Item -NewName { $_.Name -replace '^BETA_', '' }

# Dosya adının sonuna versiyon bilgisi ekleme
Get-ChildItem -Path "C:deploy" -Filter "*.config" | 
    Rename-Item -NewName { $_.BaseName + "_v2" + $_.Extension }

Where-Object { -not $_.PSIsContainer } satırına dikkat edin. Bu ifade klasörleri filtreleyip yalnızca dosyaları seçer. Toplu yeniden adlandırma işlemlerinde yanlışlıkla klasörleri de yeniden adlandırmak, çok ciddi sorunlara yol açabilir.

Hata Yönetimi ve Loglama

Prodüksiyon ortamında çalışan bir scriptte hata yönetimi zorunludur. Hangi dosyaların başarıyla yeniden adlandırıldığını ve hangilerinde hata oluştuğunu kayıt altına almak, sorun gidermeyi çok kolaylaştırır.

# Hata yönetimi ve loglama ile gelişmiş script
$logDosyasi = "C:logsrename_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
$basarili = 0
$basarisiz = 0

Add-Content -Path $logDosyasi -Value "Yeniden adlandirma islemi basladi: $(Get-Date)"

Get-ChildItem -Path "C:dataimport" -Filter "*.dat" | ForEach-Object {
    $eskiAd = $_.FullName
    $yeniAd = [System.IO.Path]::ChangeExtension($_.Name, ".csv")
    
    try {
        Rename-Item -Path $eskiAd -NewName $yeniAd -ErrorAction Stop
        $mesaj = "[BASARILI] $eskiAd -> $yeniAd"
        Add-Content -Path $logDosyasi -Value $mesaj
        $basarili++
        Write-Host $mesaj -ForegroundColor Green
    }
    catch {
        $hataMesaji = "[HATA] $eskiAd - $($_.Exception.Message)"
        Add-Content -Path $logDosyasi -Value $hataMesaji
        $basarisiz++
        Write-Host $hataMesaji -ForegroundColor Red
    }
}

$ozet = "Islem tamamlandi. Basarili: $basarili, Basarisiz: $basarisiz"
Add-Content -Path $logDosyasi -Value $ozet
Write-Host $ozet -ForegroundColor Yellow

Bu script, her adımı log dosyasına yazıyor ve konsola renk kodlu çıktı veriyor. Özellikle binlerce dosya üzerinde çalışırken bu tür bir loglama mekanizması hayat kurtarır.

Toplu İşlem Öncesi Yedekleme

Büyük çaplı yeniden adlandırma işlemlerinden önce, mevcut dosya adlarının bir listesini almanız çok akıllıca olur. Böylece bir şeyler ters giderse neyin nereye döneceğini bilirsiniz.

# Mevcut dosya adlarını kaydet
Get-ChildItem -Path "C:kritikdosyalar" -Recurse | 
    Select-Object FullName, Name, LastWriteTime |
    Export-Csv -Path "C:backupdosya_listesi_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation -Encoding UTF8

# Geri alma scripti oluşturma
$geriAlmaScript = "C:backupgeri_alma_$(Get-Date -Format 'yyyyMMdd').ps1"
Get-ChildItem -Path "C:kritikdosyalar" -Filter "*.dat" | ForEach-Object {
    "Rename-Item -Path '$($_.DirectoryName)$([System.IO.Path]::ChangeExtension($_.Name, '.csv'))' -NewName '$($_.Name)'"
} | Out-File -FilePath $geriAlmaScript -Encoding UTF8

Write-Host "Geri alma scripti olusturuldu: $geriAlmaScript"

Bu yaklaşım çok akıllıca: İşlemi yapmadan önce, işlemi geri alacak scripti oluşturuyorsunuz. Yani her şey ters gitse bile tek bir script çalıştırarak eski haline dönebilirsiniz.

Özel Bir Senaryo: Sunucu Log Arşivleme

Gerçek hayatta sıkça karşılaştığım bir senaryoyu paylaşayım. IIS veya Apache log dosyaları günlük olarak oluşturulur ve bunları aylık arşivlemek gerekebilir. Dosyalar u_ex240115.log formatındaysa, bunları okunabilir hale getirmek için şu scripti kullanabilirsiniz:

# IIS log dosyalarını okunabilir formata çevirme
# u_ex240115.log -> IIS_2024-01-15.log
Get-ChildItem -Path "C:inetpublogsLogFilesW3SVC1" -Filter "u_ex*.log" | 
    ForEach-Object {
        if ($_.BaseName -match '^u_ex(d{2})(d{2})(d{2})$') {
            $yil = "20" + $matches[1]
            $ay = $matches[2]
            $gun = $matches[3]
            $yeniAd = "IIS_$yil-$ay-$gun.log"
            
            try {
                Rename-Item -Path $_.FullName -NewName $yeniAd -ErrorAction Stop
                Write-Host "Donusturuldu: $($_.Name) -> $yeniAd" -ForegroundColor Green
            }
            catch {
                Write-Warning "Hata: $($_.Name) - $($_.Exception.Message)"
            }
        }
    }

Bu script, IIS’in kısaltılmış tarih formatını tam tarihe çeviriyor. Artık log dosyalarınız IIS_2024-01-15.log gibi görünüyor ve hangi tarihe ait olduğu hemen anlaşılıyor.

Performans İpuçları

Binlerce dosya üzerinde çalışırken performans önemli hale gelir.

  • Pipeline yerine ForEach kullanın: Çok büyük dosya listelerinde ForEach-Object yerine foreach döngüsü daha hızlı çalışabilir
  • -Filter kullanın: Where-Object ile filtreleme yapmak yerine Get-ChildItem -Filter kullanmak çok daha hızlıdır, çünkü filtreleme işletim sistemi seviyesinde yapılır
  • -Recurse’ü dikkatli kullanın: Alt dizinleri taramak zaman alır, gerçekten gerekmiyorsa kullanmayın
  • Paralel işlem: PowerShell 7 ve üzerinde ForEach-Object -Parallel ile işlemleri paralel yürütebilirsiniz
# PowerShell 7+ ile paralel yeniden adlandirma
Get-ChildItem -Path "C:buyukarsiv" -Filter "*.dat" | 
    ForEach-Object -Parallel {
        $yeniAd = [System.IO.Path]::ChangeExtension($_.Name, ".csv")
        Rename-Item -Path $_.FullName -NewName $yeniAd
    } -ThrottleLimit 10

Bu özellik özellikle network sürücülerinde veya yavaş depolama sistemlerinde büyük fark yaratabilir.

Sık Yapılan Hatalar

Toplu yeniden adlandırma sırasında insanların en çok takıldığı noktalara değinelim.

  • Çakışan dosya adları: Eğer iki farklı dosya aynı yeni ada dönüşecekse, ikincisi hata verecektir. Her zaman benzersizlik kontrolü yapın
  • Sadece okunabilir dosyalar: Bazı dosyalar kilitli veya salt okunur olabilir. -ErrorAction SilentlyContinue yerine try-catch kullanın ki hatalar gözden kaçmasın
  • UNC path sorunları: Network paylaşımlarında \sunucupaylasim formatındaki yollarla çalışırken bazı ek izinler gerekebilir
  • Büyük/küçük harf duyarlılığı: Windows dosya sistemi (NTFS) büyük/küçük harf duyarsızdır, bu yüzden sadece büyük/küçük harf farklı olan adlandırmalar sorun çıkarabilir
  • Özel karakterler: Köşeli parantez [] gibi karakterler PowerShell’de wildcard karakterleri olarak yorumlanabilir. Bu tür karakterler içeren dosya yollarını -LiteralPath parametresiyle kullanın
# LiteralPath kullanımı - özel karakterler içeren dosyalar için
Rename-Item -LiteralPath "C:dosyalarrapor[final].docx" -NewName "rapor_final.docx"

Sonuç

PowerShell ile toplu dosya yeniden adlandırma, başlangıçta karmaşık görünse de birkaç temel prensibi kavradıktan sonra inanılmaz derecede güçlü bir araç haline gelir. Get-ChildItem, Rename-Item ve regex kombinasyonu, neredeyse her türlü yeniden adlandırma senaryosunu çözebilir.

En önemli alışkanlıklar şunlar:

  • Her şeyden önce -WhatIf ile test edin
  • Büyük işlemlerden önce yedek alın veya geri alma scripti oluşturun
  • Her zaman hata yönetimi ekleyin ve işlemleri loglayın
  • Özel karakterler içeren dosya adlarında -LiteralPath kullanın
  • Klasörleri yanlışlıkla yeniden adlandırmaktan kaçınmak için Where-Object { -not $_.PSIsContainer } filtresi ekleyin

Bu scriptleri kendi ortamınıza uyarlarken, her zaman test ortamında denedikten sonra prodüksiyona taşıyın. Ve unutmayın, PowerShell’in en güzel yanı şu: Bugün yazdığınız bir script, gelecekte aynı sorunu saniyeler içinde çözebilir. Bir kez yatırım yapın, defalarca kazanın.

Yorum yapın