2

Problem

I am trying to modify a file by replacing a very specific substring within a file; however, in this particular instance, the file contains two lines that are nearly identical.

This line is in an AssemblyInfo file and I am trying to replace the value of the AssemblyVersion. See below:

$CurrentVersion = '1.0.*'
$NewVersion = '1.0.7.1'

# Two similar lines:
// [assembly: AssemblyVersion("1.0.*")] # I want to ignore this line
[assembly: AssemblyVersion("1.0.*")] # I want to target this value

I have been trying several different approaches to this, each with varying results.

$Assembly = 'C:\path\to\AssemblyInfo.cs'
$regex = '(?<!\/\/ \[assembly:AssemblyVersion\(")(?<=AssemblyVersion\(")[^"]*'
$regex2 = ('`n\[assembly: AssemblyVersion\("'+$CurrentVersion+'"\)\]')

Attempt 001

(GC $Assembly) |
ForEach-Object { $_.Replace($CurrentVersion, $NewVersion) } |
Set-Content $Assembly

This was an obvious failure. It ends up replacing both instances of '1.0.*'

Attempt 002

GC $Assembly | 
Select-String -Pattern '^\[assembly' -AllMatches | 
ForEach-Object { $_.Replace($CurrentVersion, $NewVersion) } |
Set-Content $Assembly

This ended with incompatible command issues...

Attempt 003

(GC $Assembly) | ForEAch-Object { 
    If ( $_ -MATCH $CurrentVersion ) {
        ForEach ($Line in $_) {
            $_.Replace($CurrentVersion, $NewVersion)
        }
    }
 } |
 Set-Content $Assembly

This ended up removing all lines that contained // as the starting characters... which was not what I wanted...

Attempt 004

GC $Assembly |
ForEach-Object {
    $_.Replace($regex2, ('[assembly: AssemblyVersion("'+$NewVersion+'")]'))
} |
Set-Content $Assembly

I get an error saying the file is in use... but that didn't make sense as I couldn't find anything using it...

I have tried several other paths as well, most variations of the above 4 in hopes of achieving my goal. Even going so far as to target the line using the regex line provided above and variations of it to try and grab

Question

Using PowerShell, how can I replace only the line/value of the target line (ignoring the line that begins with // ), but still keep all the lines in the file upon saving the contents?

2
  • 1
    I get an error saying the file is in use... but that didn't make sense as I couldn't find anything using it... - you are using it. Reading from it is using it. Attempt 3 add else { $_ } - if the line doesn't match the regex you still need to output it and write it. Commented Nov 14, 2017 at 20:20
  • @TessellatingHeckler O.o .... Oh... Wow... I feel... Yeah. Well that was much simpler than I had initially thought... Commented Nov 14, 2017 at 20:28

1 Answer 1

3

You are trying to use regex patterns but keep using the string method .Replace() which does not support it. You should be using the -replace operator. That would solve part of your issue.

Looks like you only want to replace the line that does not have anything else on it besides the assembly info.

$path = "C:\temp\test.txt"
$newVersion = "1.0.0.1"
$pattern = '^\s*?\[assembly: AssemblyVersion\("(.*)"\)\]'
(Get-Content $path) | ForEach-Object{
    if($_ -match $pattern){
        # We have found the matching line
        '[assembly: AssemblyVersion("{0}")]' -f $newVersion
    } else {
        # Output line as is
        $_
    }
} | Set-Content $path

That would be a verbose yet simple to follow way to do what you want. It will only match if the assembly line is at the start of the line with optional spaces.

I would expect that the pattern '^[assembly: AssemblyVersion\("(.*)"\)\]' works just as well since it should appear at the start of the line anyway.

This comes from another answer about almost this exact problem except now there is more that one match possibility. Also you will see my regex pattern isolates the current version in case you need that. If that is the case looked at the linked question.


You can combine this with other options but if you know ahead of time the version you are going to use then the replacement is pretty simple as well.

$newVersion = "1.6.5.6"
(Get-Content $path -Raw) -replace '(?m)^\[assembly: AssemblyVersion\("(.*)"\)\]', ('[assembly: AssemblyFileVersion("{0}")]' -f $newVersion)  | Set-Content $path

That reads the file in as one string and performs the replacement as long as the pattern is at the start of the line in the file. (?m) lets us treat the start of line anchor ^ as something that works at the beginning of lines in the text. Not just the start of the whole string.

Sign up to request clarification or add additional context in comments.

5 Comments

hmm... This is very elegant. Thank you.
What does the ? provide in the pattern? I've never found good use-cases for it in my regex.
@TheIncorrigible1 Because I am not sure if there is any leading whitespace. I make it optional so that it should only match lines with assembly info at the start of the line or with spaces. Its my way of avoiding dealing with the slash logic. Yes \s* would also work but I hate using greedy matches all the time because it can get you into trouble.
@TheIncorrigible1 ? does different things in different places, with \s* it makes it non-greedy; try 'abcabc' -match 'a.*c'; $matches compared to 'abcabc' -match 'a.*?c'; $matches. The first matches across a 'c' as far as possible, the other stops as soon as it satisfies the match condition. (?m) at the start sets an option for the regex engine, and ? is used in lookarounds as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.