1

I want to replace these line in my AssemblyInfo.cs encoded in UTF-8 with Windows CRLF at the end of each lines

<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release

by these

[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

To do so, I have a powershell script that will parse through all my files and do the replacement.

The regex I prepare in regex101 is this one and works on 101 :

<<<<<<<\sHEAD\n\[assembly:\sAssemblyVersion\("2\.0\.0\.0"\)\]\n\[assembly:\sAssemblyFileVersion\("2\.0\.0\.0"\)\]\n=======\n\[assembly:\sAssemblyVersion\("1\.1\.0\.0"\)\]\n\[assembly: AssemblyFileVersion\("1\.1\.0\.0"\)\]\n>>>>>>>\sv1_final_release

I can't manage to make the -replace work on the new lines. But when targeting only <<<<<<<\sHEAD, it matches and replacing is performed.

All the following variations failed :

  • <<<<<<<\sHEAD\n\[assembly: no error no replacement
  • <<<<<<<\sHEAD\r\n\[assembly: no error no replacement
  • <<<<<<<\sHEADrn\[assembly: no error no replacement, write-host prints it as <<<<<<<\sHEAD \[assembly:

It's not about /gm or (*CRLF)

My powershell instruction for info :

$ConflictVersionRegex = "<<<<<<<\sHEAD\n\[assembly:\sAssemblyVersion\(`"2\.0\.0\.0`"\)\]\n\[assembly:\sAssemblyFileVersion\(`"2\.0\.0\.0`"\)\]\n=======\n\[assembly:\sAssemblyVersion\(`"1\.1\.0\.0`"\)\]\n\[assembly: AssemblyFileVersion\(`"1\.1\.0\.0`"\)\]\n>>>>>>>\sv1_final_release" 
$ConflictVersionRegexTest = "<<<<<<<\sHEAD`r`n\[assembly:" 
$fileContent = Get-Content($filePath)   
$filecontent = $filecontent -replace $ConflictVersionRegexTest, $AssemblyNewVersion
[System.IO.File]::WriteAllLines($filePath, $fileContent, $Utf8NoBomEncoding)

What am I missing ? Why is it not replacing ?

Many thanks

1
  • Why use regex instead of just a simple replace? Also .Net uses its own little regex. Seems you made your regex in PCRE Commented Oct 15, 2018 at 15:28

2 Answers 2

4

Based on feedback from Poutrathor (the OP), there were two problems:

  • The primary problem was that Get-Content($filePath) (which should be written as
    Get-Content $filePath[1]) reads the file line by line, which results in an array of lines when captured in a variable.
    -replace then operates on each input line individually, which means that the line-spanning regex won't match anything.

    • Solution: Use Get-Content -Raw (PSv3+) to read the file as a whole into a single, multi-line string.
  • Secondarily, you mention needing to replace the regex newline (end-of-line) escape sequence (\n) (LF) with its PowerShell string-interpolation counterpart (`n) - note that PowerShell uses `, the backtick, as the escape character:

    • Note that that is only necessary in the replacement string, in order to create actual, literal newlines (line breaks) on output - as opposed to using regex construct \n for matching newlines.

    • However, on Windows, newlines are typically CRLF sequences, i.e., a CR (\r, `r) immediately followed by a LF (\n / `n) - i.e., \r\n/ `r`n - whereas on Unix-like platforms they are just LF, \n / `n.

      • If you're not sure which style of newlines given input has, use \r?\n to match newlines in a cross-platform-compatible manner.
        If you don't care what specific newlines the input has, this is safe to use methodically, as a matter of habit.
    • Therefore:

      • In your regex, while in your case you can choose between \r\n and `r`n, note that:

        • `r`n only works in double-quoted "..." strings.
        • It is generally preferable to use literal, single-quoted strings to store regexes - which requires use of \r\n (Windows) / \n (Unix) / \r?\n (platform-agnostic) - so that there's no confusion over which parts of the string PowerShell interpolates up front vs. which parts are interpreted by the regex engine.
      • In your replacement string, use `r`n inside "..." to create actual newlines.


As an alternative to using escape sequences to represent newlines, you can use here-strings to conveniently define multi-line strings with actual newlines (line breaks), as shown in Paweł Dyl's answer, but there's a caveat:

  • Here-strings invariably have the same style of newline as the enclosing script file, which means that:
    • A regex based on a here-string will only match if the input happens to have the same style of newlines as the script file.
    • A replacement string based on a here-string will invariably use the script file's newline style.

[1] Your call looks like a .NET method call and while it happens to work in this case, such syntax confusion should be avoided: PowerShell cmdlets and functions are invoked like shell commands: without parentheses ((...)) and with whitespace-separated arguments.

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

3 Comments

So is it how community answer are made ? :0 Many thanks for the in-depth explanation. Powershell is tricky.
My pleasure, @Poutrathor. PowerShell is ... uh ... powerful, so it can definitely get tricky. One of the problems here is that two unrelated worlds mingle: PowerShell's string interpolation and (.NET) regular expressions. What you do mean by community answers?
I see - thanks, @Poutrathor. In case you weren't just joking: I don't think that this particular answer is a good candidate.
0

See following demo:

$newText = @'
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
'@

$src = @'
<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release
Other lines and second instance
<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release
Some other lines
'@

$src -replace ('<<<<<<< HEAD\s+',
    '\[assembly: AssemblyVersion\("2\.0\.0\.0"\)\]\s+',
    '\[assembly: AssemblyFileVersion\("2\.0\.0\.0"\)\]\s+'+
    '=======\s+'+
    '\[assembly: AssemblyVersion\("1\.1\.0\.0"\)\]\s+',
    '\[assembly: AssemblyFileVersion\("1\.1\.0\.0"\)\]\s+'+
    '>>>>>>> v1_final_release'),$newText

Also, make sure your contents are read as one large string. This can be achieved using Get-Content $path -Raw or [System.IO.File]::ReadAllText($path).

1 Comment

Here-strings with actual line breaks are definitely convenient, but in a regex the caveat is that they invariably use the enclosing script file's style of newlines (line endings), which may or may not match the input's.

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.