1

I am trying to run a powershell script, which will replace the occurrence of a string with another string. I want to remove the angular brackets from the include and replace them with double quotes with the header file name only, without the folder path before the Header file name. Example :

#include <a/b/c/def.hpp>
#include <a/b/c/ddd.hpp> 

to

#include "def.hpp"
#include "ddd.hpp"

I have tried this.

(Get-ChildItem -Recurse | Get-Content | Select-String -Pattern "#include <a/b/c/")-replace '#include <a/b/c/(\w+).*/', '#include "'-replace '>','"'

With this, I get the desired result, but I am not able to use the result and replace the existing line in the source file.

Thanks in advance.

I have to query upto 100 files in a folder. Please suggest the best way to do this.

    INPUT 
#include <a0/b/c/d/HeaderFile1.hpp>
#include <a1/b/c/d/HeaderFile2.hpp>
#include <a2/b/c/d/HeaderFile3.hpp>
#include <a3/b/c/d/HeaderFile4.hpp>
#include <a4/b/c/d/HeaderFile5.hpp>
#include <a5/b/c/d/HeaderFile6.hpp>   


OUTPUT
#include "HeaderFile1.hpp"
#include "HeaderFile2.hpp"
#include "HeaderFile3.hpp"
#include "HeaderFile4.hpp"
#include "HeaderFile5.hpp"
#include "HeaderFile6.hpp"
2

3 Answers 3

5

I created some test files containing your samples:

> Select-String file*.cpp -patt 'hpp'

File1.cpp:1:#include <a0/b/c/d/HeaderFile1.hpp>
File2.cpp:1:#include <a1/b/c/d/HeaderFile2.hpp>
File3.cpp:1:#include <a2/b/c/d/HeaderFile3.hpp>
File4.cpp:1:#include <a3/b/c/d/HeaderFile4.hpp>
File5.cpp:1:#include <a4/b/c/d/HeaderFile5.hpp>
File6.cpp:1:#include <a5/b/c/d/HeaderFile6.hpp>

The following script uses a RegEx explained live on RegEx101 and below static.

## Q:\Test\2018\06\09\SO_50777494.ps1
## the following RegEx usees a positive lookbehind and a
## capture group for the filename.

[RegEx]$Search = '(?<=#include ).*\/([^>]+)>'
$Replace = '"$1"'

ForEach ($File in (Get-ChildItem -Path '.\File*.cpp' -Recurse -File)) {
    (Get-Content $File) -Replace $Search,$Replace |
        Set-Content $File
}

Sample output of a Select-String after replace:

> sls file*.cpp -patt 'hpp'

File1.cpp:1:#include "HeaderFile1.hpp"
File2.cpp:1:#include "HeaderFile2.hpp"
File3.cpp:1:#include "HeaderFile3.hpp"
File4.cpp:1:#include "HeaderFile4.hpp"
File5.cpp:1:#include "HeaderFile5.hpp"
File6.cpp:1:#include "HeaderFile6.hpp"

Explanation of the RegEx:

(?<=#include ).*\/([^>]+)>
  Positive Lookbehind (?<=#include )
  Assert that the Regex below matches
  #include matches the characters #include literally 
  .*
    . matches any character (except for line terminators)
    * Quantifier — Matches between zero and unlimited times, 
      as many times as possible, giving back as needed (greedy)
  \/ matches the character / literally (case sensitive)
  1st Capturing Group ([^>]+)
    Match a single character not present in the list below [^>]+
    + Quantifier — Matches between one and unlimited times, 
      as many times as possible, giving back as needed (greedy)
  > matches the character > literally (case sensitive)
Sign up to request clarification or add additional context in comments.

Comments

2

You simply need to write the new text back to the file. here is my way of doing it:

Get-ChildItem -Recurse -File | ForEach-Object {

    (Get-Content $_).replace('#include <a/b/c/def.hpp>','#include "def.hpp"') | Set-Content $_
}

Edit:

Now that you have explained your question, it is clear you need to use a regular expression. the one LotPings has posted will work just fine. so using his RegEx the correct answer is:

Get-ChildItem -Recurse -File | ForEach-Object {

    (Get-Content $_)-replace('(?<=#include ).*\/([^>]+)>','"$1"') | Set-Content $_
}

3 Comments

The pattern for the header file , example <a/b/c/def.hpp> is not fixed. It can be #include <a/b/c/ddd.hpp> also. So I need to write a general query
@SuryaRao Can you please explain exactly how the headers look like?
The idea is to remove the angular brackets from the incude line and replace it with double quotes. And also emit the folder hierarchy in the include line.
1

Edit: tested on the examples you provided, if format might be different (deeper path, special characters in path and so on) please test before. And have a backup

Something like this should work:

$items = Get-ChildItem -Recurse
foreach ($i in $items) {
 $newContent = (Get-Content $i) -replace('#include <((\w)+\/)*(\w+\.hpp)>','#include "$3"')
 $newContent | Set-Content $i
}

11 Comments

After executing the above command, it is asking for input. cmdlet Get-Content at command pipeline position 1 Supply values for the following parameters: Path[0]:
@LotPings thanks for your comment. I'll remember to do this in future. Just corrected it and tested.
@SuryaRao please check now I just corrected and added parenthesis
@robdy : The pattern for the header file , example <a/b/c/def.hpp> is not fixed. It can be #include <a/b/c/ddd.hpp> also. So I need to write a general query.
I'll have to use simple regex to capture all 4 options (if I understand correctly what you want to replace can be either #include <a.hpp> or #include <b.hpp> or #include <c.hpp> or #include <def.hpp>). I just edited my original answer to add this
|

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.