Reputation: 47
I have a list of files all with the extension .pkg as part of a product. Within each of those files is a section of text that looks like this:
//VERSION
1.73
//END VERSION
I am trying to write a PowerShell script that will find that block of code and replace the 1.73 to 1.74.
//VERSION
1.74
//END VERSION
What I have found is the following, but I cannot seem to get the regex portion write to identify the block of code.
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$dir = $scriptPath + "\Sandbox\"
Write-Host "*** Modifying Files In $dir to Update Version ***"
Get-ChildItem -Path $dir | ?{$_.Extension -eq ".pkg"} |
ForEach-Object {
$copyto = $dir + $_
# Load the file's contents, replace commas with spaces
(Get-Content $copyto) -replace '(?<=//VERSION\r").*?(?="\r//END VERSION)', '1.73' |
# and write it to the correct folder and file name
Out-File $copyto
}
I know the basic syntax works as I am using this exact same code to loop through and replace something else more easily identified.
UPDATED WORKING CODE
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$dir = Join-Path $scriptPath "Sandbox"
Write-Host "*** Modifying Files In $dir to Update Version ***"
Get-ChildItem -Path $dir | ?{$_.Extension -eq ".pkg"} |
ForEach-Object {
$copyto = Join-Path $dir $_
$foundLine = $false
Get-Content $copyto | foreach {
if ($foundLine) {
# Flag is set. Output the following instead of the line from the file
'1.74'
# And clear the flag
$foundLine = $false
} else {
# Output current line
$_
# If we find the version line, set the flag so that
# we enter the replacement on the next line.
if ($_ -eq '//VERSION') {
$foundLine = $true
}
}
} | Out-File ($copyto + '.new')
Write-Host "*** Deleting $copyto ***"
Remove-Item $copyto
Write-Host "*** Renaming ($copyto + '.new') ***"
Rename-Item -Path ($copyto + '.new') -NewName $copyto
}
Upvotes: 1
Views: 547
Reputation: 9266
By default, Get-Content
returns the file as an array of strings. Each line is checked independently against your regex. Since the pattern you're trying to match crosses more than one line, no single line will ever match the whole thing.
To treat the entire file as one string with embedded newlines, pass the -Raw
parameter
(Get-Content $copyto -Raw) -replace ...
Here is an alternate solution that operates line by line and doesn't use a regex. Longer to type, but easier to understand for people not used to regular expressions.
$foundLine = $false
Get-Content $copyto | foreach {
if ($foundLine) {
# Flag is set. Output the following instead of the line from the file
'1.74'
# And clear the flag
$foundLine = $false
} else {
# Output current line
$_
# If we find the version line, set the flag so that
# we enter the replacement on the next line.
if ($_ -eq '//VERSION') {
$foundLine = $true
}
}
} | Out-File ($copyto + '.new')
Unrelated: When concatenating paths, prefer Join-Path
over string concatenation. It helps avoid missing backslash issues and makes your code more robust.
$dir = Join-Path $scriptPath "Sandbox"
...
$copyto = Join-Path $dir $_
Upvotes: 1
Reputation: 59031
Like Ryan showed you, you have to use the -raw
switch for the Get-Content cmdlet. However, there are some more errors within your regex:
\r?\n
to read a new line unless you are know what new line character is used. So replace this line:
(Get-Content $copyto -raw) -replace '(?<=\/\/VERSION)(\r?\n).*?(\r?\n)(?=\/\/END VERSION)', '${1}1.74${2}'
Upvotes: 1