使用 Powershell 替换多个文件和文件夹中的多个字符串
Using Powershell to replace multiple strings in multiple files & folders
我在 CSV 文件中有一个字符串列表。格式为:
OldValue,NewValue
223134,875621
321321,876330
....
并且该文件包含几百行(每个 OldValue 都是唯一的)。我需要处理多个文件夹和子文件夹中多个文本文件的更改。我对文件夹、文件和文本行数的最佳猜测是 - 15 个文件夹,每个文件夹中大约有 150 个文本文件,每个文件夹中大约有 65,000 行文本(每个文本文件 400-500 行)。
我将对数据进行 2 次传递,除非我可以一次完成。第一步是生成一个文本文件,我将用作检查列表来检查我的更改。第二遍是实际对文件进行更改。另外,我只想更改出现字符串的文本文件(不是每个文件)。
我正在使用以下 Powershell 脚本浏览文件并生成所需更改的列表。该脚本运行,但速度非常慢。我还没有研究替换逻辑,但我认为它与我所拥有的相似。
# replace a string in a file with powershell
[reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic") | Out-Null
Function Search {
# Parameters $Path and $SearchString
param ([Parameter(Mandatory=$true, ValueFromPipeline = $true)][string]$Path,
[Parameter(Mandatory=$true)][string]$SearchString
)
try {
#.NET FindInFiles Method to Look for file
[Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles(
$Path,
[Microsoft.VisualBasic.FileIO.SearchOption]::SearchAllSubDirectories,
$SearchString
)
} catch { $_ }
}
if (Test-Path "C:\Work\ListofAllFilenamesToSearch.txt") { # if file exists
Remove-Item "C:\Work\ListofAllFilenamesToSearch.txt"
}
if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists
Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt"
}
$filefolder1 = "C:\TestFolder\WorkFiles"
$ftype = "*.txt"
$filenames1 = Search $filefolder1 $ftype
$filenames1 | Out-File "C:\Work\ListofAllFilenamesToSearch.txt" -Width 2000
if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists
Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt"
}
(Get-Content "C:\Work\NumberXrefList.CSV" |where {$_.readcount -gt 1}) | foreach{
$OldFieldValue, $NewFieldValue = $_.Split("|")
$filenamelist = (Get-Content "C:\Work\ListofAllFilenamesToSearch.txt" -ReadCount 5) #|
foreach ($j in $filenamelist) {
#$testvar = (Get-Content $j )
#$testvar = (Get-Content $j -ReadCount 100)
$testvar = (Get-Content $j -Delimiter "\n")
Foreach ($i in $testvar)
{
if ($i -imatch $OldFieldValue) {
$j + "|" + $OldFieldValue + "|" + $NewFieldValue | Out-File "C:\Work\FilesThatNeedToBeChanged.txt" -Width 2000 -Append
}
}
}
}
$FileFolder = (Get-Content "C:\Work\FilesThatNeedToBeChanged.txt" -ReadCount 5)
Get-ChildItem $FileFolder -Recurse |
select -ExpandProperty fullname |
foreach {
if (Select-String -Path $_ -SimpleMatch $OldFieldValue -Debug -Quiet) {
(Get-Content $_) |
ForEach-Object {$_ -replace $OldFieldValue, $NewFieldValue }|
Set-Content $_ -WhatIf
}
}
在上面的代码中,我尝试了 Get-Content
- default
、-ReadCount
和 -Delimiter
的几种方法 - 试图避免出局内存错误。
我唯一能控制的是新旧替换字符串文件的长度。有没有办法在 Powershell 中执行此操作?还有更好的option/solution吗?我是运行Windows7、Powershell 3.0.
您的主要问题是您一遍又一遍地阅读文件以更改每个条款。您需要反转替换项的循环和文件的循环。另外,预加载 csv。类似于:
$filefolder1 = "C:\TestFolder\WorkFiles"
$ftype = "*.txt"
$filenames = gci -Path $filefolder1 -Filter $ftype -Recurse
$replaceValues = Import-Csv -Path "C:\Work\NumberXrefList.CSV"
foreach ($file in $filenames) {
$contents = Get-Content -Path $file
foreach ($replaceValue in $replaceValues) {
$contents = $contents -replace $replaceValue.OldValue, $replaceValue.NewValue
}
Copy-Item $file "$file.old"
Set-Content -Path $file -Value $contents
}
我在 CSV 文件中有一个字符串列表。格式为:
OldValue,NewValue
223134,875621
321321,876330
....
并且该文件包含几百行(每个 OldValue 都是唯一的)。我需要处理多个文件夹和子文件夹中多个文本文件的更改。我对文件夹、文件和文本行数的最佳猜测是 - 15 个文件夹,每个文件夹中大约有 150 个文本文件,每个文件夹中大约有 65,000 行文本(每个文本文件 400-500 行)。
我将对数据进行 2 次传递,除非我可以一次完成。第一步是生成一个文本文件,我将用作检查列表来检查我的更改。第二遍是实际对文件进行更改。另外,我只想更改出现字符串的文本文件(不是每个文件)。
我正在使用以下 Powershell 脚本浏览文件并生成所需更改的列表。该脚本运行,但速度非常慢。我还没有研究替换逻辑,但我认为它与我所拥有的相似。
# replace a string in a file with powershell
[reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic") | Out-Null
Function Search {
# Parameters $Path and $SearchString
param ([Parameter(Mandatory=$true, ValueFromPipeline = $true)][string]$Path,
[Parameter(Mandatory=$true)][string]$SearchString
)
try {
#.NET FindInFiles Method to Look for file
[Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles(
$Path,
[Microsoft.VisualBasic.FileIO.SearchOption]::SearchAllSubDirectories,
$SearchString
)
} catch { $_ }
}
if (Test-Path "C:\Work\ListofAllFilenamesToSearch.txt") { # if file exists
Remove-Item "C:\Work\ListofAllFilenamesToSearch.txt"
}
if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists
Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt"
}
$filefolder1 = "C:\TestFolder\WorkFiles"
$ftype = "*.txt"
$filenames1 = Search $filefolder1 $ftype
$filenames1 | Out-File "C:\Work\ListofAllFilenamesToSearch.txt" -Width 2000
if (Test-Path "C:\Work\FilesThatNeedToBeChanged.txt") { # if file exists
Remove-Item "C:\Work\FilesThatNeedToBeChanged.txt"
}
(Get-Content "C:\Work\NumberXrefList.CSV" |where {$_.readcount -gt 1}) | foreach{
$OldFieldValue, $NewFieldValue = $_.Split("|")
$filenamelist = (Get-Content "C:\Work\ListofAllFilenamesToSearch.txt" -ReadCount 5) #|
foreach ($j in $filenamelist) {
#$testvar = (Get-Content $j )
#$testvar = (Get-Content $j -ReadCount 100)
$testvar = (Get-Content $j -Delimiter "\n")
Foreach ($i in $testvar)
{
if ($i -imatch $OldFieldValue) {
$j + "|" + $OldFieldValue + "|" + $NewFieldValue | Out-File "C:\Work\FilesThatNeedToBeChanged.txt" -Width 2000 -Append
}
}
}
}
$FileFolder = (Get-Content "C:\Work\FilesThatNeedToBeChanged.txt" -ReadCount 5)
Get-ChildItem $FileFolder -Recurse |
select -ExpandProperty fullname |
foreach {
if (Select-String -Path $_ -SimpleMatch $OldFieldValue -Debug -Quiet) {
(Get-Content $_) |
ForEach-Object {$_ -replace $OldFieldValue, $NewFieldValue }|
Set-Content $_ -WhatIf
}
}
在上面的代码中,我尝试了 Get-Content
- default
、-ReadCount
和 -Delimiter
的几种方法 - 试图避免出局内存错误。
我唯一能控制的是新旧替换字符串文件的长度。有没有办法在 Powershell 中执行此操作?还有更好的option/solution吗?我是运行Windows7、Powershell 3.0.
您的主要问题是您一遍又一遍地阅读文件以更改每个条款。您需要反转替换项的循环和文件的循环。另外,预加载 csv。类似于:
$filefolder1 = "C:\TestFolder\WorkFiles"
$ftype = "*.txt"
$filenames = gci -Path $filefolder1 -Filter $ftype -Recurse
$replaceValues = Import-Csv -Path "C:\Work\NumberXrefList.CSV"
foreach ($file in $filenames) {
$contents = Get-Content -Path $file
foreach ($replaceValue in $replaceValues) {
$contents = $contents -replace $replaceValue.OldValue, $replaceValue.NewValue
}
Copy-Item $file "$file.old"
Set-Content -Path $file -Value $contents
}