Belirli Bir Uzantıya Sahip Tüm Dosyaları Bulmak ve Değiştirmek için PowerShell Betiği


124

Windows Server 2008'de aşağıdaki gibi iç içe geçmiş birkaç yapılandırma dosyam var:

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

Yapılandırmamda aşağıdaki gibi bir dizge değiştirmem gerekiyor:

<add key="Environment" value="Dev"/>

Olacak:

<add key="Environment" value="Demo"/>

Toplu komut dosyası kullanmayı düşündüm, ancak bunu yapmanın iyi bir yolu yoktu ve PowerShell komut dosyası ile bunu kolayca yapabileceğinizi duydum. Bul / değiştir örnekleri buldum, ancak C: \ Projects dizinimdeki tüm klasörleri dolaşacak ve '.config' uzantısıyla biten dosyaları bulacak bir yol bulmayı umuyordum. Bir tane bulduğunda, dize değerlerimi değiştirmesini istiyorum.

Bunun nasıl yapılacağını öğrenmek için herhangi bir iyi kaynak veya biraz bilgi sunabilecek herhangi bir PowerShell guru var mı?


1
Nasıl başladığınızı veya ele alınması gereken dosyalarda bazı garip biçimlendirme sorunları olup olmadığını bize bildirin.
Sorunla

Yanıtlar:


178

İşte kafamın tepesine ilk girişim.

$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Dev", "Demo" } |
    Set-Content $file.PSPath
}

3
Tüm sunulan çözümlerin test edildiğini eklemek isterim, ancak bu, okunabilirlik açısından en kolay olanıydı. Bunu bir iş arkadaşıma verebildim ve neler olduğunu kolayca anlayabildi. Yardımın için teşekkürler.
Brandon

11
Bunun alt dizinlerdeki dosyalarda çalışması için ".PSPath" gerekir. İlginç bir şekilde, bu işi get-content etrafında bir () olmadan yapmaya çalıştığımda, yazma-içeriğinde başarısız oldu çünkü dosya kilitliydi.
Frank Schwieterman

25
Kısa versiyon (yaygın takma adlar kullanılır):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Artyom

5
@Artyom .sonrasını unutma ls. Buna ben de soktum.
Pureferret

5
UnauthorizedAccessException ayrıca tüm dosyalarda çalıştırmak için * .config dosyasını kaldırırsanız klasörlerden kaynaklanabilir. Get-ChildItem'e -File filtresi ekleyebilirsiniz ... Bunu anlamak biraz zaman aldı
Amir Katz

32

PowerShell iyi bir seçimdir;) Verilen dizindeki dosyaları numaralandırmak, okumak ve işlemek çok kolaydır.

Komut dosyası şöyle görünebilir:

Get-ChildItem C:\Projects *.config -recurse |
    Foreach-Object {
        $c = ($_ | Get-Content) 
        $c = $c -replace '<add key="Environment" value="Dev"/>','<add key="Environment" value="Demo"/>'
        [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
    }

Sizin için okunabilmesi için kodu daha fazla satıra böldüm. Bunun yerine Set-Content kullanabileceğinizi unutmayın [IO.File]::WriteAllText, ancak bu, sonuna yeni bir satır ekler. Onunla bundan WriteAllTextkaçınabilirsiniz.

Aksi takdirde kodu şöyle görünebilir: $c | Set-Content $_.FullName.


14

Bu yaklaşım iyi çalışıyor:

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • "Eski" ve "yeni" yi karşılık gelen değerlere değiştirin (veya değişkenleri kullanın).
  • Parantezi unutmayın - bu olmadan bir erişim hatası alırsınız.

Bu yüzden kısa ve öz ifadesi için bu bir ile gitti - ama değiştirmek zorunda kaldık Get-Content $_ile Get-Content $_.FullNameve eşdeğer Set-Contento kökünde değildi dosyaları işlemek için.
Matt Whitfield

11

Xml ve xpath ile giderdim:

dir C:\Projects\project_*\project*.config -recurse | foreach-object{  
   $wc = [xml](Get-Content $_.fullname)
   $wc.SelectNodes("//add[@key='Environment'][@value='Dev']") | Foreach-Object {$_.value = 'Demo'}  
   $wc.Save($_.fullname)  
}

11

Bu powershell örneği, bir klasördeki ve alt klasörlerindeki "\ foo \" dizesinin tüm örneklerini arar, "\ foo \" yu "\ bar \" ile değiştirir VE "\ foo \ dizesini içermeyen dosyaları YENİDEN YAZMAZ "Bu şekilde, dizenin bulunmadığı son güncelleme tarih saat damgalarını dosyayı yok etmezsiniz:

Get-ChildItem  -Path C:\YOUR_ROOT_PATH\*.* -recurse 
 | ForEach {If (Get-Content $_.FullName | Select-String -Pattern '\\foo\\') 
           {(Get-Content $_ | ForEach {$_ -replace '\\foo\\', '\bar\'}) | Set-Content $_ }
           }

7

@Artyom'un yorumunu yararlı buldum ama maalesef bir yanıt göndermedi.

Bu, benim görüşüme göre kabul edilen cevabın en iyi versiyonu olan kısa versiyondur;

ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}

1
Benim yaptığım gibi - bunu doğrudan bir toplu iş dosyasından yürütmek isteyen - başka birinin bununla karşılaşması durumunda, böyle bir komutu çalıştırırken takma ad foreach-objectyerine kullanmak yardımcı olabilir %. Aksi takdirde, şu hatayla sonuçlanabilir:Expressions are only allowed as the first element of a pipeline
Dustin Halstead

Kısa her zaman daha iyi değildir, uzun versiyonda ne olduğu daha açıktır.
Nick N.

@NickN. Peki, doğru. Hedefinizin ne olduğuna bağlı. POB olarak işaretlerdim;)
M

6

Bir dosyadaki metni değiştirmek için küçük bir yardımcı işlev yazdım:

function Replace-TextInFile
{
    Param(
        [string]$FilePath,
        [string]$Pattern,
        [string]$Replacement
    )

    [System.IO.File]::WriteAllText(
        $FilePath,
        ([System.IO.File]::ReadAllText($FilePath) -replace $Pattern, $Replacement)
    )
}

Misal:

Get-ChildItem . *.config -rec | ForEach-Object { 
    Replace-TextInFile -FilePath $_ -Pattern 'old' -Replacement 'new' 
}

4

Özyinelemeli değiştirme yapılırken, yol ve dosya adının dahil edilmesi gerekir:

Get-ChildItem -Recurse | ForEach {  (Get-Content $_.PSPath | 
ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }

Bu, geçerli dizininizdeki klasörlerinizin tüm dosyalarındaki tüm "eski" yi "yeni" büyük / küçük harf duyarlı olarak değiştirecektir.


Cevabınızın ".PSPath" kısmı bana gerçekten yardımcı oldu. Ama içteki "{$" kısmını "$ _" olarak değiştirmem gerekiyordu. Cevabınızı düzenlerdim, ancak -creplace bölümünü kullanmıyorum
aaaa bbbb
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.