brun_exe
brun_exe

Reputation: 17

Replace multiple lines on multiple files with the same pattern on Powershell

I've been trying to replace a pattern of lines from multiple .xml files using Powershell scripts

For some reason, I still couldn't figure out how to do that even for a single file, this is what I've been using

$loc = file
$file = Get-Content $loc -Raw
$file -replace "(\<!-- First.*?\/>)","blank"

And these are the lines that I want to replace

<!-- First -->
<prop classproperty="1"
     packageproperty="1.2"
/>

Basically I've been trying to use -Raw property for the Powershell read into a single line and the tried to replace using the regex expression (\<!-- Weather.*?\/>). I want to replace from the commentary to the prop tag closure. But every time I run my script, it changes nothing. What am I doing wrong? I ran this regex code, with the file on a single line using the https://regexr.com/ and it works perfectly

Also how can I apply the same change for multiple files? I tried to this, but it returns an error telling me that I don't have permission to the path

$loc = Get-Location
Get-ChildItem -Path $loc\*.* -Filter '*_property.xml' -Recurse | ForEach-Object {
    (Get-Content -path $loc -Raw) -replace '(\<!-- Weather.*?\/>)','white'
}

Thanks!

Upvotes: 0

Views: 391

Answers (2)

G42
G42

Reputation: 10019

Although this answer by thepip3r provides a solution to your regex, I wouldn't recommend dealing with xml this way - see RegEx match open tags except XHTML self-contained tags.


A better way would be to use the [xml] type:

$fileContents = @"
<!-- First -->
<prop classproperty="1"
    packageproperty="1.2"
/>
"@

$xml = [xml]$fileContents

# specify the comment you want to replace in the where statement
$xml.ChildNodes | where {($_.Name -match '#comment' -and $_.value -match 'First')} | ForEach-Object {
    # you can add a new comment
    $child = $xml.CreateComment(" New Comment ")
    [void]$_.ParentNode.AppendChild($child)

    # or you could just remove
    [void]$_.ParentNode.RemoveChild($_)
}

# specify what else you want to remove in the where statement
$xml.ChildNodes | where {$_.name -eq 'prop' -and $_.classproperty -eq '1'} | ForEach-Object {
    [void]$_.ParentNode.RemoveChild($_)
}

$xml.Save('C:\temp\newxml.xml')

Change Get-Content -path $loc to Get-Content $_.Fullname in your ForEach-Object (to specify the current file. otherwise you're trying to get the contents of the directory $loc)

Upvotes: 1

thepip3r
thepip3r

Reputation: 2935

You need to add (?ms) to the front of your RegEx:

$file -replace "(?ms)(\<!-- First.*?\/>)","blank"

This tells the RegEx engine to allow your wildcards to span multi-line.

The reason it worked on the site you mentioned and not when you copied it directly into your script is because there are a few different RegEx engines that interpret RegEx syntax. You'll notice that the website you linked lists two different engines in the upper right-hand corner of the page with Javascript being default and PCRE being in the drop-down.

.NET claims support for PERL 5's Regular Expression engine:

In .NET, regular expression patterns are defined by a special syntax or language, which is compatible with Perl 5 regular expressions and adds some additional features such as right-to-left matching.

https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions

..but this is a bit misleading since PERL provides support for PCRE in version 5.10:

As of Perl 5.10.0, Perl supports several Python/PCRE-specific extensions to the regex syntax. While Perl programmers are encouraged to use the Perl-specific syntax, the following are also accepted

https://perldoc.perl.org/perlre.html#PCRE%2fPython-Support

so my guess would be that .NET implemented Perl-style expressions prior to PCRE support and thus, the website you're using may not accurately reflect when a particular regular expression will successfully validate in the website you posted, at all. At the very least, it has a high likelihood to be consistently inconsistent with it's results in PowerShell.

The problem with your loop and multiple files is obviously permissions (evidenced by the exception). Get rights to the file location and if you have them, run your script in an elevated context (right-click-> Run As Administrator)

Upvotes: 1

Related Questions