PowerShell Script для пошуку та заміни всіх файлів із конкретним розширенням


123

У мене на сервері Windows Server 2008 вкладено кілька файлів конфігурації:

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

У моїй конфігурації мені потрібно зробити заміну рядка таким чином:

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

стане:

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

Я думав про використання пакетного сценарію, але не було хорошого способу це зробити, і я чув, що за допомогою сценаріїв PowerShell ви можете це легко виконати. Я знайшов приклади пошуку / заміни, але сподівався на спосіб, який пройде всі папки в моєму каталозі C: ​​\ Projects і знайде будь-які файли, що закінчуються розширенням '.config'. Коли він знайде його, я хочу, щоб він замінив мої рядкові значення.

Будь-які хороші ресурси, щоб дізнатися, як це зробити, чи будь-які гуру PowerShell, які можуть запропонувати деяку інформацію?


1
Повідомте нас, як ви потрапили або якщо були якісь незвичайні проблеми із форматуванням файлів, які потрібно було вирішити. Хороша річ у тому, що це тест, не впливаючи на виробничий код
Robben_Ford_Fan_boy

Відповіді:


178

Ось перша спроба у верхній частині голови.

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

3
Я хотів би додати, що тестування розроблених рішень все працювало, але це було найпростішим для читання. Я зміг передати це колезі, і він міг легко зрозуміти, що відбувається. Дякуємо за допомогу.
Брендон

11
Для цього для роботи у файлах у підкаталогах вам потрібен ".PSPath". Цікаво, що коли я намагався зробити цю роботу без () навколо get-контенту, він не зміг у контент-записі, оскільки файл був заблокований.
Френк Швітерман

25
Коротка версія (використовуються поширені псевдоніми):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Артем

5
@Artyom не забувай .після цього ls. Я сам був застрелений цим.
Pureferret

5
UnauthorizedAccessException також може спричинити папки, якщо ви видалите * .config для запуску всіх файлів. Ви можете додати фільтр -файл до Get-ChildItem ... Знадобився час, щоб зрозуміти це
Амір Кац

32

PowerShell - це хороший вибір;) Перераховувати файли в заданому каталозі дуже легко, читати їх та обробляти.

Сценарій може виглядати так:

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

Я розділив код на кілька рядків, щоб бути читабельним для вас. Зауважте, що ви можете використовувати Set-Content замість [IO.File]::WriteAllText, але він додає новий рядок наприкінці. З цим WriteAllTextможна уникнути.

В іншому випадку код може виглядати наступним чином : $c | Set-Content $_.FullName.


14

Цей підхід працює добре:

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • Змініть "старий" і "новий" на відповідні їм значення (або використовуйте змінні).
  • Не забувайте дужки - без яких ви отримаєте помилку доступу.

Так що я пішов з цим для ємного вирази - але мені довелося замінити Get-Content $_з Get-Content $_.FullName, а еквівалент Set-Contentдля того , щоб обробляти файли , які не були в корені.
Метт Вітфілд,

11

Я б пішов з xml та 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)  
}

11

Цей приклад повноважень шукає всі екземпляри рядка "\ foo \" у папці та її вкладених папках, замінює "\ foo \" на "\ bar \" І НЕ перезаписує файли, що не містять рядок "\ foo \ "Таким чином, ви не знищуєте штампи дати останнього оновлення файлу там, де рядок не знайдено:

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 корисним, але, на жаль, він не опублікував відповіді.

Це коротка версія, на мій погляд, найкраща версія прийнятої відповіді;

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

1
У випадку, якщо хтось інший зіткнеться з цим, як я це робив - прагнучи виконати це безпосередньо з пакетного файлу - Це може допомогти використовувати foreach-objectзамість %псевдоніму при виконанні такої команди. В іншому випадку це може призвести до помилки:Expressions are only allowed as the first element of a pipeline
Дастін Холстед

Короткий не завжди кращий, більш зрозуміло, що відбувається в довгому варіанті.
Нік Н.

@NickN. Ну правильно. Це залежить від того, яка ваша мета. Я би
позначив

6

Я написав трохи допоміжної функції для заміни тексту у файлі:

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

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

Приклад:

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

4

Виконуючи рекурсивну заміну, слід вказати шлях та ім'я файлу:

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

Це замінить усі "старі" на "нові" з урахуванням регістру у всіх файлах ваших папок поточного каталогу.


Частина вашої відповіді ".PSPath" мені дуже допомогла. Але мені довелося змінити внутрішнє "{$" на "$ _". Я б відредагував вашу відповідь, але я не використовую вашу -заміщую частину - я використовую прийняту відповідь з .PSPath
aaaa bbbb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.