powershell replace text between 2 lines - powershell

I have a text file and want to replace the text between 2 lines. This is working OK until there is no text between the two lines.
my code:
$File = "D:\test.txt"
$NewLine = "newline with some text"
$text = Get-Content "D:\test.txt" -raw
$text -replace ('(?m)(.*)^Line 3[\r\n]+Line 4([\r\n])', $NewLine) |
Out-File $File -Force
Text files:
Text file that does work:
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Text file that does NOT work:
Line 1
Line 2
Line 3
Some text here
Line 4
Line 5
Line 6
What I am doing wrong?

The issue you have is that your Regular Expression (RegEx for short) does not allow for anything between Line 3 and Line 4. Here's what you're using (cleaned up a tiny bit):
(?m)^Line 3[\r\n]+Line 4[\r\n]
Let us break that down just a little bit. (?m) puts the RegEx engine into multi-line mode, so it will allow you to use the carat ^ to indicate the beginning of a line, instead of just the beginning of the string. There's other things it does, but that's what you are using it for. I discarded the (.*), because it is pointless. So you have a carat indicating the beginning of a line, followed by the text Line 3, and then [\r\n]+ which will find the end of the line. Then it finds Line 4, immediately followed by the end of that line. This is why it fails when there is text, you didn't allow for any additional text. To do that you can use .*?. What that means is this: The dot indicates any character, doesn't matter if it's letter, number, symbol, or even a character that doesn't register on the screen. The asterisk * indicates that there needs to be zero or more of that, so zero or more of any character. The question mark tells it to look for zero or more, but to match as few characters as possible before moving on in the pattern, so it will only match things until it can move on to Line 4[\r\n]. The functional pattern would be:
(?m)^Line 3[\r\n]+.*?[\r\n]Line 4[\r\n]

Corrected your regex here to capture only Line 3 and Line 4 with the text inbetween.
$File = 'D:\test.txt'
$NewLine = 'newline with some text'
$text = Get-Content -Path 'D:\test.txt' -Raw
$Pattern = '(?m)^Line\s3[\r\n]+.*?Line\s4'
$text -replace $Pattern,$NewLine |
Out-File -FilePath $File -Force

Related

Powershell- How to wrap a specific line in an array object in double quotes?

Lets say I have a text file whose content is this:
======= Section 1 ==========
This is line one
This is line two
This is line three
======= Section 2 ==========
This is line Four
This is line Five
This is line Six
I import it into Powershell using $SourceFile = Get-Content '.\source.txt' -Delimiter '==='
then clean it up with $Objects = $SourceFile -replace '^=.*', ''
I now have this $Objects array that has:
This is line one
This is line two
This is line three
and
This is line Four
This is line Five
This is line Six
What I really want to know is how can I wrap a specific line in double quotes or parenthesis etc, so that specific line in all the objects arrays are also handled the same way.
For example line 3 of both arrays should have double quotes wrapped around them:
This is line one
This is line two
"This is line three"
and
This is line Four
This is line Five
"This is line Six"
I have tried many things, closest I got was $test = $objects -replace 'This is line three', '"This is line three"' As you can see this is less than ideal for an object of many arrays.
I am still fairly new to Powershell, any help would be greatly appreciated
Assuming that you want to wrap the 3rd line in each block in double quotes:
# Read the input file and split it into blocks of lines by a
# regex matching the section lines (without including the section lines).
(Get-Content -Raw file.txt) -split '(?m)^===.*\r?\n' -ne '' |
ForEach-Object { # Process each block of lines (multi-line string)
# Split this block into individual lines, ignoring a trailing newline.
$linesInBlock = $_ -replace '\r?\n\z' -split '\r?\n'
# Modify the 3rd line.
$linesInBlock[2] = '"{0}"' -f $linesInBlock[2]
# Output the lines, including the modification.
Write-Verbose -Verbose "-- next block"
$linesInBlock
}
Output with your sample input:
VERBOSE: -- next block
This is line one
This is line two
"This is line three"
VERBOSE: -- next block
This is line Four
This is line Five
"This is line Six"

Remove a line using PowerShell

I would like to remove a empty line in using powershell.
Here is my Text
Line 1
Line 2
<Empty line>
<Empty line>
Line 3
Line 4
I have tried using -replace ("`n", "") but it does not work.
Please help!
$text = """
Line 1
Line 2
Line 3
Line 4
"""
$text -replace "(?m)^\s*`r`n",''
# Or
$text -replace "(?m)^\s*`n",''
regex explained:
(?m) set the multiline flag so ^ matches the beginning of each line and not just the begining of the whole string.
^ matches the beginning of a line
\s* matches any number of white spaces (could also be 0 white spaces)
`r`n matches the end of a line.
Why `r`n or just `n (depending on the machine or file):
\`r = CR (Carriage Return) → Used as a new line character in Mac OS before X
\`n = LF (Line Feed) → Used as a new line character in Unix/Mac OS X
\`r\`n = CR + LF → Used as a new line character in Windows
Or simply read the file as string array and use a Where-Object to let pass only the lines that contain at least one non-whitespace character.
$text = Get-Content -Path 'D:\Thefile.log' |
Where-Object { $_ -match '\S' }
Now you can save this string array in $text back to file, join it with [environment]::NewLine to become a single string again, or...

Read a file, count delimiters and output line number with mismatched delimiter

I have a file named: test_file.txt. The second line has 4 pipe delimiters and all other lines except 2nd line has 3 pipe delimiters.
I just want to output line 2 since it has one more delimiter than other lines.
$colCnt = "C:\test.txt"
[int]$LastSplitCount = $Null
Get-Content $colCnt | ?{$_} | Select -Skip 1 | %{if($LastSplitCount -and !
($_.split("|").Count -eq $LastSplitCount))
{"Process stopped at line number $($_.psobject.Properties.value[5]) for column count mis-match.";break}
elseif(!$LastSplitCount){$LastSplitCount = $_.split("|").Count}}
If your text file looks anything like this:
blah|using|three|delimiters
blah|using|four |delimiter |characters
blah|using|three|delimiters
blah|using|four |delimiter |characters
blah|using two |delimiters
The the following code should output the lines with more (or less) than 3 | delimiters:
$line = 0
switch -Regex -File "C:\test.txt" {
'^(?:[^|]*\|){3}[^|]*$' { $line++ } # this line is OK, just increase the line counter
default { "Bad delimiter count in line {0}: '{1}'" -f ++$line, $_ }
}
Output:
Bad delimiter count in line 2: 'blah|using|four |delimiter |characters'
Bad delimiter count in line 4: 'blah|using|four |delimiter |characters'
Bad delimiter count in line 5: 'blah|using two |delimiters'
Regex details:
^ Assert position at the beginning of the string
(?: Match the regular expression below
[^|] Match any character that is NOT a “|”
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\| Match the character “|” literally
){3} Exactly 3 times
[^|] Match any character that is NOT a “|”
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)

Find lines between a pattern, and append 1st line to lines

I have the following case I'm trying to script in Powershell. I have done this exercise using Sed on a bash terminal, but having trouble writing in Powershell. Any help would be greatly appreciated.
(sed -r -e '/^N/h;/^[N-]/d;G;s/(.*)\n(.*)/\2 \1/' <file>, with a file format without < and > chars. surrounding the first letter on each line)
The start pattern always start with a <N> (only 1 instance per block), lines between start with a <J>, and the end pattern is always --
--------------
<N>ABC123
<J>SomethingHere1
<J>SomethingHere2
<J>SomethingHere3
-------------- <-- end of section
I'm trying to take the first line in each section <N> and copy it AFTER each <J> in the same section. For example:
<J>SomethingHere1 <N>ABC123
<J>SomethingHere2 <N>ABC123
<J>SomethingHere3 <N>ABC123
The number of <J> lines per section can vary (0-N). In a case with no <J>, nothing needs to be done.
Powershell version:5.1.16299.611
The following, pipeline-based solution isn't fast, but conceptually straightforward:
Get-Content file.txt | ForEach-Object {
if ($_ -match '^-+$') { $newSect = $true }
elseif ($newSect) { $firstSectionLine = $_; $newSect = $False }
else { "{0}`t{1}" -f $_, $firstSectionLine }
}
It reads and processes lines one by one (with the line at hand reflected in automatic variable $_.
It uses a regex (^-+) with the -match operator to identify section dividers; if found, flag $newSect is set to signal that the next line is the section's first data line.
If the first data line is hit, it is cached in variable $firstSectionLine, and the $newSect flag is reset.
All other lines are by definition the lines to which the first data line is to be appended, which is done via the -f string-formatting operator, using a tab char. (`t) as the separator.
Here's a faster PSv4+ solution that is more complex, however, and it reads the entire input file into memory up front:
((Get-Content -Raw file.txt) -split '(?m)^-+(?:\r?\n)?' -ne '').ForEach({
$firstLine, $otherLines = $_ -split '\r?\n' -ne ''
foreach ($otherLine in $otherLines) { "{0}`t{1}" -f $otherLine, $firstLine }
})
Get-Content -Raw reads in the input file in full, as a single string.
It uses the -split operator to split the input file into sections, and then processes each section.
Regex '(?m)^-+(?:\r?\n)?' matches a section divider line, optionally followed by a newline.
(?m) is the multiline option, which makes ^ and $ match the start and end of each line, respectively:
\r?\n matches a newline, either in CRLF (\r\n) or LF-only (\n) form.
(?:...) is a non-capturing group; making it non-capturing prevents what it matches from being included in the elements returned by -split.
-ne '' filters out resulting empty elements.
-split '\r?\n' splits each section into individual lines.
If performance is still a concern, you could speed up reading the file with [IO.File]::ReadAllText("$PWD/file.txt").

Edit text between two lines using powershell

I want to change this text
PortNumber=10001
;UserName=xxxxxxxxx
;Password=xxxxxxxxx
CiPdfPath=xxxxx
into this
PortNumber=10001
UserName=xxxxxxxxx
Password=xxxxxxxxx
CiPdfPath=xxxxx
I cannot simply search for ;Username=xxxx and ;Password=xxxx because they exist multiple times in the file and need to be commented on some places.
I found the next command
$file = Get-Content "Test.ini" -raw
$file -replace "(?m)^PortNumber=10001[\n\r]+;UserName=xxxx[\r\n]+;Password=xxxx","PortNumber=10001 `r`nUserName=xxxxx`r`nPassword=xxxxx"
And it worked!
But maybe it can be simplyfied
If you use the (?ms) (multiline-singleline) option and here-strings, you can do most of the work with copy/paste:
$string =
#'
PortNumber=10001
;UserName=xxxxxxxxx
;Password=xxxxxxxxx
CiPdfPath=xxxxx
'#
$regex =
#'
(?ms)PortNumber=10001
;UserName=xxxxxxxxx
;Password=xxxxxxxxx
CiPdfPath=xxxxx
'#
$replace =
#'
PortNumber=10001
UserName=xxxxxxxxx
Password=xxxxxxxxx
CiPdfPath=xxxxx
'#
$string -replace $regex,$replace
PortNumber=10001
UserName=xxxxxxxxx
Password=xxxxxxxxx
CiPdfPath=xxxxx
Why don't you search full text which you'd like to replace?
So find:
PortNumber=10001
;UserName=xxxxxxxxx
;Password=xxxxxxxxx
CiPdfPath=xxxxx
and replace with:
PortNumber=10001
UserName=xxxxxxxxx
Password=xxxxxxxxx
CiPdfPath=xxxxx
You can use regular expression to express irrelevant characters
http://www.regular-expressions.info/powershell.html
http://www.powershelladmin.com/wiki/Powershell_regular_expressions
You could use Regex.
Or even simpler, depending on your requirement;
If you know the linenumber of the lines you want to replace, you could easily do this do replace the certain lines:
Given that the file format is the text you've pasted (e.g. username on line 2 and password on line 3), read the file into a line buffer. Replace line 2 and 3 and set the content back to the file.
$lines=(Get-Content .\Test.txt)
$lines[1]= $lines[1].Replace(";","")
$lines[2]= $lines[2].Replace(";","")
$lines|Set-Content .\Test.txt
I might be misunderstading the nature of the question but are you not simply trying to remove the leading semicolons? Is it important to seach for those strings exclusivley?
$file = Get-Content "Test.ini" -raw
$file -replace "(?sm)^;"
$file -replace "(?smi)^;(?=(username|password))"
Both examples should produce the same output. The first will match all leading semicolons. The second will match leading semicolons if the are followed, using a lookahead, by either username or password.