ivzhh
ivzhh

Reputation: 330

Powershell: Split-Path Not Working in -Replace

I want to pre-process C code, replacing #include "../../abc/efg/hij.h" with "hij.h".

I use command

'#include "../../abc/efg/hij.h"' -replace '(#include\s+)(".*\w+\.h")',"$(Split-Path -Leaf `$2)"

To do the replacement. However, no matter how I tried (use single-/double- quote, $(), and other methods), the final string (I did not Set-Content yet, just want to see the result in standard output) is still "../../abc/efg/hij.h".

When I run Split-Path -Leaf "../../abc/efg/hij.h", the result is hij.h, which is correct.

Can some one please tell me why this happens? Thank you very much!

Upvotes: 3

Views: 459

Answers (2)

TessellatingHeckler
TessellatingHeckler

Reputation: 29033

Seems weird that you are doing path parsing with regex, but then trying to use Split-Path to do it with a commandlet as well. Why not do the replace entirely with regexes?

Get rid of the bit you don't want by capturing it and replacing it with nothing:

PS C:\> '#include "../../abc/efg/hij.h"' -replace '(?<=#include\s+")(.*/)(?=\w+\.h")'
#include "hij.h"

or keep the bit you do want by capturing it and putting it into a new line:

PS C:\> '#include "../../abc/efg/hij.h"' -replace '#include\s+".*/(.*\.h)"','#include "$1"'
#include "hij.h"

Upvotes: 4

Ryan Bemrose
Ryan Bemrose

Reputation: 9266

The problem you are seeing is that the -replace operator matches the regex and performs the replacement atomically. It provides no method to run any PowerShell code in between the two operations.

In your code, the scriptblock (Split-Path -Leaf $2) evaluates first, before the regex has been matched. The result of that scriptblock ($null, the empty string, or an exception depending on what value the variable $2 holds) is then passed to -replace, with observed result.

What you want is to be able to run a PowerShell command in between matching the regex and replacing it. One way to do this is to use -match to match the regular expression, and then build up your replacement string using the matches in the $matches variable.

$oldLine -match '#include\s+"(.*\w+\.h)"'
$newLine = '#include "{0}"' -f (split-path -leaf $matches[1])

Optionally you can do the replacement using the -replace operator, rather than building the whole line from scratch. However, because it evaluates the regex again, it can lead to performance problems for large input files.

Upvotes: 2

Related Questions