Skrip PowerShell untuk Menemukan dan Mengganti untuk semua File dengan Ekstensi Tertentu

124

Saya memiliki beberapa file konfigurasi di Windows Server 2008 yang bersarang seperti:

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

Dalam konfigurasi saya, saya perlu melakukan penggantian string seperti:

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

akan menjadi:

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

Saya berpikir untuk menggunakan skrip batch, tetapi tidak ada cara yang baik untuk melakukan ini, dan saya mendengar bahwa dengan skrip PowerShell Anda dapat dengan mudah melakukan ini. Saya telah menemukan contoh temukan / ganti, tetapi saya berharap cara yang akan melintasi semua folder dalam direktori C: \ Projects saya dan menemukan file apa pun yang diakhiri dengan ekstensi '.config'. Ketika menemukan satu, saya ingin itu mengganti nilai string saya.

Adakah sumber daya yang bagus untuk mencari tahu cara melakukan ini atau pakar PowerShell apa pun yang dapat menawarkan wawasan?

Brandon
sumber
1
Beri tahu kami bagaimana Anda mendapatkan atau jika ada beberapa masalah pemformatan yang aneh dengan file yang perlu ditangani. Satu hal yang baik tentang masalah ini adalah bahwa itu menguji tanpa mempengaruhi kode produksi
Robben_Ford_Fan_boy

Jawaban:

178

Ini percobaan pertama di atas kepalaku.

$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Dev", "Demo" } |
    Set-Content $file.PSPath
}
Robben_Ford_Fan_boy
sumber
3
Saya ingin menambahkan bahwa pengujian solusi yang diberikan semuanya berfungsi, tetapi yang ini adalah yang paling mudah untuk dibaca. Saya dapat menyerahkan ini kepada rekan kerja dan dia dapat dengan mudah memahami apa yang sedang terjadi. Terima kasih atas bantuannya.
Brandon
11
Untuk bekerja dalam file di subdirektori, Anda membutuhkan ".PSPath". Menariknya, ketika saya mencoba membuat ini berfungsi tanpa () di sekitar get-content, gagal menulis konten karena file dikunci.
Frank Schwieterman
25
Versi pendek (alias umum digunakan):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Artyom
5
@Artyom jangan lupa .setelah ls. Tersengat oleh itu sendiri.
Pureferret
5
UnauthorizedAccessException juga dapat menyebabkan karena folder jika Anda akan menghapus * .config untuk dijalankan di semua file. Anda dapat menambahkan -File filter ke Get-ChildItem ... Butuh beberapa saat untuk mengetahuinya
Amir Katz
32

PowerShell adalah pilihan yang baik;) Sangat mudah untuk menghitung file dalam direktori tertentu, membacanya dan memprosesnya.

Skripnya akan terlihat seperti ini:

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"))
    }

Saya membagi kode menjadi lebih banyak baris agar dapat dibaca untuk Anda. Perhatikan bahwa Anda dapat menggunakan Set-Content, bukan [IO.File]::WriteAllText, tetapi menambahkan baris baru di akhir. Dengan WriteAllTextAnda bisa menghindarinya.

Jika tidak kode bisa terlihat seperti ini: $c | Set-Content $_.FullName.

stej
sumber
14

Pendekatan ini bekerja dengan baik:

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • Ubah "lama" dan "baru" ke nilai yang sesuai (atau gunakan variabel).
  • Jangan lupa tanda kurung - tanpanya Anda akan menerima kesalahan akses.
Tolga
sumber
Jadi saya menggunakan yang ini untuk ekspresi singkat - tetapi saya harus menggantinya Get-Content $_dengan Get-Content $_.FullName, dan yang setara Set-Contentuntuk itu untuk menangani file yang tidak ada di root.
Matt Whitfield
11

Saya akan menggunakan xml dan xpath:

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)  
}
Shay Levy
sumber
11

Contoh PowerShell ini mencari semua contoh string "\ foo \" dalam folder dan subfoldernya, menggantikan "\ foo \" dengan "\ bar \" DAN TIDAK MENGUBAH file yang tidak berisi string "\ foo \ "Dengan cara ini Anda tidak menghancurkan file stempel waktu pembaruan terakhir di mana string tidak ditemukan:

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 $_ }
           }
Kaye
sumber
7

Saya menemukan komentar @Artyom berguna tetapi sayangnya dia belum memposting jawaban.

Ini adalah versi singkat, menurut saya versi terbaik, dari jawaban yang diterima;

ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}
M--
sumber
1
Jika ada orang lain yang menjalankan ini, seperti yang saya lakukan - ingin mengeksekusi ini langsung dari file batch - Mungkin membantu untuk menggunakan foreach-objectalih-alih %alias saat menjalankan perintah seperti ini. Jika tidak, ini dapat menyebabkan kesalahan:Expressions are only allowed as the first element of a pipeline
Dustin Halstead
Pendek tidak selalu lebih baik, lebih jelas apa yang terjadi di versi panjang.
Nick N.
@Tokopedia Baiklah. Itu tergantung pada apa tujuan Anda. Saya akan menandainya sebagai POB;)
M--
6

Saya telah menulis sedikit fungsi pembantu untuk mengganti teks dalam file:

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

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

Contoh:

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

Saat melakukan penggantian rekursif, jalur dan nama file perlu disertakan:

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

Ini akan mengganti semua "lama" dengan "baru" case-sensitive di semua file folder Anda di direktori Anda saat ini.

Geir Ivar Jerstad
sumber
Bagian ".PSPath" dari jawaban Anda sangat membantu saya. Tapi saya harus mengubah bagian dalam "{$" menjadi "$ _". Saya akan mengedit jawaban Anda, tetapi saya tidak menggunakan bagian -creplace Anda - Saya menggunakan jawaban yang diterima dengan .PSPath
aaaa bbbb