somal
somal

Reputation: 53

Powershell replace part of content of one file by content of another file

I have text1.txt file with following content:

sometext_1
--start
sometext_2.1
sometext_2.2
...
sometext_2.n
--end
sometext_3

and text2.txt file with following content:

--start
sometext_2_new1
sometext_2_new2
...
sometext_2_newn
--end

Could someone help me to write Powershell code to update content of text1.txt file between keywords --start and --end with all content of file text2.txt? So the new text1.txt will look like below:

sometext_1
--start
sometext_2_new1
sometext_2_new2
...
sometext_2_newn
--end
sometext_3

UPDATE: I'm really new in Powershell. After some research I was able to came up with following which is still not giving me what I need:

param(
[string]$location
)

$contentStart = Get-Content "c:\text2.txt"

$configFiles = Get-ChildItem $location\*.txt -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "--start.*?(--end)", $contentStart } |
    Out-File $file.PSPath
}

But it works only if text1.txt has keywords --start and --end in one line and relult is following (inserted content do not have endline symbols):

sometext_1
--start sometext_2_new1 sometext_2_new2 ... sometext_2_newn --end
sometext_3

Upvotes: 1

Views: 1454

Answers (2)

user6811411
user6811411

Reputation:

I like Regex101.com to test and refine the Regular Expression I develop.
See here your example

You need to tell the RegEx with:

  • m flag that ^ and $ match line begin/end
  • s flag that the . dot also matches new line

PS> $text1 = Get-Content ".\text1.txt"
PS> $text2 = Get-Content ".\text2.txt"
PS> $text1 -replace "(?ms)^--start$.*?^--end$",$text2
sometext_1
--start
sometext_2.1
sometext_2.2
...
sometext_2.n
--end
sometext_3

Upvotes: 2

Bacon Bits
Bacon Bits

Reputation: 32230

It's a bit involved because you need to create a regular expression with SingleLine mode.

Try something like this:

foreach ($file in $configFiles)
{
    Get-Content $file.PSPath -Raw |
    ForEach-Object { $_ -replace "(?s)(--start`r`n).*?(`r`n--end)", "`$1$contentStart`$2" } |
    Out-File $file.PSPath
}

Explanation:

The -Raw switch for Get-Content tells the cmdlet to get everything as one single string, rather than an array of lines like it normally does.

There's only one item going through the pipeline now for each file, but we still need the ForEach-Object cmdlet to enumerate the item for us.

Now, to explain the regex:

"(?s)(--start`r`n).*?(`r`n--end)"

The (?s) is the regular expression option for single line mode. This means that the . character will match line endings. Normally it doesn't.

Next, there's the rest of the regex, (--start`r`n).*?(`r`n--end). The --start and --end are pretty obvious. The Windows line ending characters are `r is expressed as \r for carriage return in most other languages and `n is \n for end of line in most other languages. They need to be included in order for the .*? not to eat them in single line mode. The parentheses are for grouping for backreferences. The first parenthetic group will become $1, and the second will become $2, etc.

Note that if your text file is not using Windows line endings you may need to modify this regex appropriately.

The replace string:

"`$1$contentStart`$2"

Here, we're using double quotes so that the PowerShell variable, $contentStart can be inlined into the string. And $1 and $2 are present as the backreference variables to the groups in the regular expression. However, we run into a problem with the backreference variables. They're $1 and $2, which are valid variables in PowerShell. So, we need to escape the dollar sign with another backtick.

Upvotes: 3

Related Questions