Використовуючи PowerShell, я хочу замінити всі точні події [MYID]
в даному файлі на MyValue
. Який найпростіший спосіб зробити це?
Використовуючи PowerShell, я хочу замінити всі точні події [MYID]
в даному файлі на MyValue
. Який найпростіший спосіб зробити це?
Відповіді:
Використання (версія V3):
(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt
Або для V2:
(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
(Get-Content file.txt) |
Foreach-Object {$_ -replace '\[MYID\]','MyValue'} |
Out-File file.txt
Зверніть увагу, що навколо (Get-Content file.txt)
потрібні дужки :
Без дужок зміст зчитується по одному рядку і протікає вниз по конвеєру до тих пір, поки не потрапить у файл-файл або контент-вміст, який намагається записати в той самий файл, але він вже відкритий get-content і ви отримуєте помилка. Дужки призводять до того, що операція зчитування вмісту повинна бути виконана один раз (відкрити, прочитати та закрити). Тільки тоді, коли всі рядки прочитані, вони прокладаються по одному, і коли вони дістають останню команду в конвеєрі, їх можна записати у файл. Це те саме, що $ content = content; $ вміст | де ...
Set-Content
замість Out-File
yuou, ви отримуєте попередження типу "Процес не може отримати доступ до файлу" 123.csv ", оскільки він використовується іншим процесом." .
Get-Content
дужками це працює. Чи можете ви пояснити у своїй відповіді, чому потрібні дужки? Я б ще замінити Out-File
з , Set-Content
тому що це безпечніше; він захищає вас від витирання цільового файлу, якщо ви забудете дужки.
Set-Content
замість цього Out-File
набагато краще та безпечніше рішення. Вибачте, потрібно подати заявку.
Я вважаю за краще використовувати клас File .NET і його статичні методи, як показано в наступному прикладі.
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
Це має перевагу в роботі з одним String замість масиву String, як із Get-Content . Методи також забезпечують кодування файлу (UTF-8 BOM тощо), без того, щоб вам доводилося піклуватися більшу частину часу.
Крім того, методи не псують закінчення рядків (закінчення рядків Unix, які можуть бути використані) на відміну від алгоритму, що використовує Get-Content і пропускає через Set-Content .
Тож для мене: менше речей, які можуть зламатися роками.
Маловідома при використанні класів .NET полягає в тому, що, коли ви ввели "[System.IO.File] ::" у вікні PowerShell, ви можете натиснути Tabклавішу, щоб перейти до методів, які існують там.
[System.IO.File] | gm
C:\Windows\System32\WindowsPowerShell\v1.0
?
Описаний вище працює лише для "одного файлу", але ви також можете запустити це для кількох файлів у вашій папці:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
foreach
можна зробити цеGet-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
foreach
, тому що Get-Content робить те, чого ви можете не очікувати ... Він повертає масив рядків, де кожна рядок є рядком у файлі. Якщо ви переглядаєте каталог (і підкаталоги), які знаходяться в іншому місці, ніж ваш запущений сценарій, вам потрібно щось подібне: Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }
де $Directory
знаходиться каталог, що містить файли, які ви хочете змінити.
Це те, що я використовую, але на великих текстових файлах це повільно.
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
Якщо ви збираєтеся замінювати рядки у великих текстових файлах, і швидкість викликає занепокоєння, зверніть увагу на використання System.IO.StreamReader та System.IO.StreamWriter .
try
{
$reader = [System.IO.StreamReader] $pathToFile
$data = $reader.ReadToEnd()
$reader.close()
}
finally
{
if ($reader -ne $null)
{
$reader.dispose()
}
}
$data = $data -replace $stringToReplace, $replaceWith
try
{
$writer = [System.IO.StreamWriter] $pathToFile
$writer.write($data)
$writer.close()
}
finally
{
if ($writer -ne $null)
{
$writer.dispose()
}
}
(Код вище не перевірений.)
Мабуть, є більш елегантний спосіб використання StreamReader і StreamWriter для заміни тексту в документі, але це повинно дати вам хорошу вихідну точку.
Я знайшов маловідомий, але дивовижно класний спосіб зробити це з Windows Powershell в дії . Ви можете посилатися на файли, як на змінні, схожі на $ env: path, але вам потрібно додати фігурні дужки.
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
$myFile
?
$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
Якщо вам потрібно замінити рядки в декількох файлах:
Слід зазначити, що різні способи, розміщені тут, можуть бути різно різними щодо часу, необхідного для його завершення. Для мене регулярно є велика кількість невеликих файлів. Щоб перевірити, що найефективніше, я витягнув 5,52 ГБ (5,933,604,999 байт) XML у 40 693 окремих файлів і пробіг три відповіді, які я знайшов тут:
## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files)
#### Test 1 - Plain Replace
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 103.725113128333
#>
#### Test 2 - Replace with -Raw
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 10.1600227983333
#>
#### Test 3 - .NET, System.IO
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
$txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ")
[System.IO.File]::WriteAllText("$xml", $txt)
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 5.83619516833333
#>
Кредит на @ rominator007
Я перетворив його на функцію (тому що ви можете скористатися нею знову)
function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
$content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
[System.IO.File]::WriteAllText("$FullPathToFile", $content)
}
ПРИМІТКА: Це НЕ чутливе до регістру !!!!!
Дивіться цю публікацію: String.Замініть ігнорування випадку
Це працювало для мене, використовуючи поточний робочий каталог в PowerShell. Вам потрібно скористатися FullName
властивістю, інакше вона не працюватиме у версії 5. PowerShell. Мені потрібно було змінити цільову версію .NET Framework у ВСІХ моїх CSPROJ
файлах.
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
Трохи старий і інший, як мені потрібно було змінити певний рядок у всіх примірниках певного імені файлу.
Крім того, Set-Content
не було повернення послідовних результатів, тому мені довелося вдатися Out-File
.
Код нижче:
$FileName =''
$OldLine = ''
$NewLine = ''
$Drives = Get-PSDrive -PSProvider FileSystem
foreach ($Drive in $Drives) {
Push-Location $Drive.Root
Get-ChildItem -Filter "$FileName" -Recurse | ForEach {
(Get-Content $_.FullName).Replace($OldLine, $NewLine) | Out-File $_.FullName
}
Pop-Location
}
Ось що найкраще працювало для мене у цій версії PowerShell:
Майор.Мінор.Бул.Ревізія
5.1.16299.98
Невелика корекція команди Set-Content. Якщо шуканий рядок не знайдений, Set-Content
команда буде порожнім (порожнім) цільовий файл.
Ви можете спочатку перевірити, існує чи ні шукана рядок. Якщо ні, то це нічого не замінить.
If (select-string -path "c:\Windows\System32\drivers\etc\hosts" -pattern "String to look for") `
{(Get-Content c:\Windows\System32\drivers\etc\hosts).replace('String to look for', 'String to replace with') | Set-Content c:\Windows\System32\drivers\etc\hosts}
Else{"Nothing happened"}
set-content test.txt "hello hello world hello world hello"
а потім (get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt
не буде порожнім файлом, як запропоновано в цьому.