3

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!

1
  • Separating paths can be done completely with regex, but I think the more interesting problem is how to manipulate the match groups using PS commands prior to replacement. Commented Dec 1, 2015 at 5:19

2 Answers 2

4

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"
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, later I did like this. Just wondering why it happens. New to Powershell, and still confused. Your solution is the better way, thanks!
2

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.

1 Comment

Your answer is very inspiring, thank you! I finally have something naive: get-content a.h | ForEach-Object {$_ -match '#include\s+"(.*\w+\.h)"' > $null; if ($matches[1]) { "#include "$(split-path -leaf $matches[1])"" } else { $_ } } | add-content b.h; Move-Item -Force b.h a.h

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.