PowerShell kullanarak, [MYID]
belirli bir dosyadaki tüm kesin oluşumları ile değiştirmek istiyorum MyValue
. Bunu yapmanın en kolay yolu nedir?
PowerShell kullanarak, [MYID]
belirli bir dosyadaki tüm kesin oluşumları ile değiştirmek istiyorum MyValue
. Bunu yapmanın en kolay yolu nedir?
Yanıtlar:
Kullanım (V3 sürümü):
(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt
Veya V2 için:
(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
Parantezlerin (Get-Content file.txt)
gerekli olduğunu unutmayın:
Parantez olmadan içerik her seferinde bir satır okunur ve aynı dosyaya yazmaya çalışan dosya dışına veya set içeriğine ulaşana kadar ardışık düzenden aşağı akar, ancak get-content tarafından zaten açıktır ve bir hata. Parantez, içerik okuma işleminin bir kez (açma, okuma ve kapatma) yapılmasına neden olur. Ancak o zaman tüm satırlar okunduklarında, her seferinde bir tane borulanırlar ve boru hattındaki son komuta ulaştıklarında dosyaya yazılabilirler. $ Content = content ile aynı; $ içerik | nerede ...
Set-Content
yerine Out-File
gibi yuou bir uyarı olsun "başka bir işlem tarafından kullanıldığı için işlem dosyası '123.csv' erişemez." .
Get-Content
parantez içinde çalışıyor. Cevabınızda parantezin neden gerekli olduğunu açıklayabilir misiniz? Hala yerini alacak Out-File
olan Set-Content
daha güvenli çünkü; parantezi unutursanız sizi hedef dosyayı silmekten korur.
Set-Content
Bunun yerine kullanmak Out-File
çok daha iyi ve daha güvenli bir çözümdür. Üzgünüm intikam almak zorunda.
Aşağıdaki örnekte görüldüğü gibi .NET File sınıfını ve statik yöntemlerini kullanmayı tercih ederim.
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
Bunun avantajı Get-Content gibi String dizesi yerine tek bir String ile çalışabilmesidir . Yöntemler , çoğu zaman ilgilenmek zorunda kalmadan dosyanın kodlamasını da (UTF-8 BOM , vb.) Halleder .
Ayrıca yöntemler Get-Content kullanan bir algoritmanın aksine satır sonlarını (kullanılabilecek Unix satır sonları) bozmaz ve Set-Content'e aktarır .
Yani benim için: Yıllar boyunca kırılabilecek daha az şey.
.NET sınıflarını kullanırken az bilinen bir şey, PowerShell penceresinde "[System.IO.File] ::" yazdığınızda, Taboradaki yöntemler arasında gezinmek için tuşa basabilmenizdir.
[System.IO.File] | gm
C:\Windows\System32\WindowsPowerShell\v1.0
?
Yukarıdaki dosya yalnızca "Bir Dosya" için çalışır, ancak bunu klasörünüzdeki birden çok dosya için de çalıştırabilirsiniz:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
foreach
bunu yapabilirsinizGet-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
foreach
, çünkü Get-Content beklemediğiniz bir şey yapar ... Her dizenin dosyada bir çizgi olduğu bir dizeler dizisi döndürür. : Eğer çalışan komut daha farklı bir konumda olduğu bir dizin (ve alt dizinleri) arasında döngü ediyorsanız, böyle bir şey isteyeceksiniz değiştirmek istediğiniz dosyaları içeren dizindir. Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }
$Directory
Kullandığım şey bu, ancak büyük metin dosyalarında yavaş.
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
Büyük metin dosyalarındaki dizeleri değiştirecekseniz ve hız önemliyse, System.IO.StreamReader ve System.IO.StreamWriter kullanmaya bakın .
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()
}
}
(Yukarıdaki kod test edilmemiştir.)
Bir belgedeki metni değiştirmek için StreamReader ve StreamWriter'ı kullanmanın muhtemelen daha zarif bir yolu vardır, ancak bu size iyi bir başlangıç noktası vermelidir.
Payette'in Windows Powershell in Action'ından bunu yapmak için biraz bilinen ama inanılmaz güzel bir yol buldum . $ Env: path benzeri değişkenler gibi dosyalara başvurabilirsiniz, ancak süslü parantez eklemeniz gerekir.
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
$myFile
?
$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
Birden Çok Dosyadaki Dizeleri Değiştirmeniz Gerekiyorsa:
Burada yayınlanan farklı yöntemlerin tamamlanması için geçen süre bakımından çılgınca farklı olabileceğine dikkat edilmelidir. Benim için düzenli olarak çok sayıda küçük dosyam var. En yüksek performansı gösteren testi yapmak için 40.693 ayrı dosyada 5.52 GB (5.933.604.999 bayt) XML çıkardım ve burada bulduğum üç yanıttan geçtim:
## 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'ye kredi
Bir işleve sardım (çünkü tekrar kullanmak isteyebilirsiniz)
function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
$content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
[System.IO.File]::WriteAllText("$FullPathToFile", $content)
}
NOT: Bu büyük / küçük harfe duyarlı DEĞİLDİR !!!!!
Bu yazıyı görün: String.Roring ignoring case
Bu benim için PowerShell'deki geçerli çalışma dizinini kullanarak çalıştı. FullName
Özelliği kullanmanız gerekir , yoksa PowerShell sürüm 5'de çalışmaz. TÜM CSPROJ
dosyalarımdaki hedef .NET framework sürümünü değiştirmem gerekiyordu .
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
Biraz eski ve farklı, çünkü belirli bir dosya adının tüm örneklerinde belirli bir satırı değiştirmem gerekiyordu.
Ayrıca, Set-Content
tutarlı sonuçlar döndürmüyordu, bu yüzden başvurmak zorunda kaldımOut-File
.
Aşağıdaki kod:
$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
}
Bu PowerShell sürümünde benim için en uygun olan buydu:
Ana.Alt.Yapı.Düzeltme
5.1.16299.98
Set-Content komutu için küçük düzeltme. Aranan dize bulunmazsa,Set-Content
komut hedef dosyayı boşaltır (boşaltır).
İlk olarak aradığınız dizenin var olup olmadığını doğrulayabilirsiniz. Değilse hiçbir şeyin yerini almayacaktır.
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"
ve daha sonra (get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt
bu dosyada önerildiği gibi dosyayı boşaltmaz.