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"
SuryaRao
  • 51
  • 1
  • 6
  • Please check post https://stackoverflow.com/questions/2837785/powershell-script-to-find-and-replace-for-all-files-with-a-specific-extension, it provides an example on how to find and replace a string in multiple files. – Evandro de Paula Jun 09 '18 at 19:31
  • 1
    Possible duplicate of [PowerShell Script to Find and Replace for all Files with a Specific Extension](https://stackoverflow.com/questions/2837785/powershell-script-to-find-and-replace-for-all-files-with-a-specific-extension) – Evandro de Paula Jun 09 '18 at 19:43

3 Answers3

4

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)
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 $_
}
Moshe perez
  • 1,626
  • 1
  • 8
  • 17
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
}
Robert Dyjas
  • 4,979
  • 3
  • 19
  • 34
  • 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]: – SuryaRao Jun 09 '18 at 20:06
  • @LotPings thanks for your comment. I'll remember to do this in future. Just corrected it and tested. – Robert Dyjas Jun 09 '18 at 20:11
  • @SuryaRao please check now I just corrected and added parenthesis – Robert Dyjas Jun 09 '18 at 20:12
  • 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 ` or `#include ` or `#include ` or `#include `). I just edited my original answer to add this – Robert Dyjas Jun 09 '18 at 20:25
  • @rodby : Let me explain it with an example here about my expectation. Note : a0, a1, a2, a3 at the beginning. 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. INPUT #include #include #include #include OUTPUT #include "HeaderFile1.hpp" #include "HeaderFile2.hpp" #include "HeaderFile3.hpp" – SuryaRao Jun 09 '18 at 20:39
  • @LotPings : I have just edited my question with an example. Hope it explains. Thanks – SuryaRao Jun 09 '18 at 20:46
  • I have tried the below, with which i get the desired output, but the rest of the contents of the file is erased and only the desired header line is modified. `$items = Get-ChildItem -Recurse foreach ($i in $items) { $newContent = ((Get-Content $i) | Select-String -Pattern "#include ','"' $newContent | Set-Content $i }` – SuryaRao Jun 09 '18 at 20:56
  • I think I managed to construct regex for it, please check the edit – Robert Dyjas Jun 09 '18 at 21:09
  • @robdy : I tried the regex . it works fine. thanks. Can we also exclude conversion of certain lines in some cases here using regex ? Example : Any include that starts with " MyFolder" should not change the header include line. `#include ' should not be altered – SuryaRao Jun 09 '18 at 22:25
  • It's also possible but I'd suggest you to make some effort and try it yourself. Your initial question is already answered and please remember that we're here to help and not to write entire code for you. https://regexr.com/ is a brilliant place to help you (hint: negated set could be helpful). Good luck! – Robert Dyjas Jun 10 '18 at 06:20