Powershell search through two lines - powershell

I have following Input lines in my notepad file.
example 1 :
//UNION TEXT=firststring,FRIEND='ABC,Secondstring,ABAER'
example 2 :
//UNION TEXT=firststring,
// FRIEND='ABC,SecondString,ABAER'
Basically, one line can span over two or three lines. If last character is , then it is treated as continuation character.
In example 1 - Text is in one line.
In example 2 - same Text is in two lines.
In example 1, I can probably write below code. However, I do not know how to do this if 'Input text' spans over two or three lines based on continuation character ,
$result = Get-Content $file.fullName | ? { ($_ -match firststring) -and ($_ -match 'secondstring')}
I think I need a way so that I can search text in multipl lines with '-and' condition. something like that...
Thanks!

You could read the entire content of the file, join the continued lines, and then split the text line-wise:
$text = [System.IO.File]::ReadAllText("C:\path\to\your.txt")
$text -replace ",`r`n", "," -split "`r`n" | ...

# get the full content as one String
$content = Get-Content -Path $file.fullName -Raw
# join continued lines, split content and filter
$content -replace '(?<=,)\s*' -split '\r\n' -match 'firststring.+secondstring'

If file is large and you want to avoid loading entire file into memory you might want to use good old .NET ReadLine:
$reader = [System.IO.File]::OpenText("test.txt")
try {
$sb = New-Object -TypeName "System.Text.StringBuilder";
for(;;) {
$line = $reader.ReadLine()
if ($line -eq $null) { break }
if ($line.EndsWith(','))
{
[void]$sb.Append($line)
}
else
{
[void]$sb.Append($line)
# You have full line at this point.
# Call string match or whatever you find appropriate.
$fullLine = $sb.ToString()
Write-Host $fullLine
[void]$sb.Clear()
}
}
}
finally {
$reader.Close()
}
If file is not large (let's say < 1G) Ansgar Wiechers answer should do the trick.

Related

Requirement is to add 4 line if the matching pattern followed by the next pattern is unmatched along with count number in power shell

Hello my input file will be like below,my requiremnet is to add 4 line if the macthing pattern folled by the next pattern is unmacthed along with count number.
i will check look for the socket and if matches will incrremnt the line count to +1 toi get the next line and look for the word "address",if the address is not present i need to insert a set of line "communication.manageraddress_9,communication.manageraddress_10,communication.manageraddress_11" netx to the line.
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2
output will be like this
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
communication.manageraddress_9=xxx.com
communication.managerid_9=xxx
communication.managerport_9=xxx
communication.socket_9=plain
communication.manageraddress_10=xxx.com
communication.managerid_10=xxx
communication.managerport_1o=xxx
communication.socket_1o=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2
this my script and i am struck with insert into text file along with increment number,can some one help in power shell.
$files = $File = 'C:\Users\rseerala\Desktop\ARUN\in.txt'
#$NewContent = Get-Content -Path $File
foreach($file in $files){
$content = Get-Content $file
for($i = 0; $i -lt $content.Count; $i++){
$line = $content[$i]
if ($line.Contains("socket"))
{
$line = $content[$i+2]
if ($line.Contains("address"))
{
Write-Host "This line starts with 6"
}}}}
Ok, so if I understand correctly, this is what you want:
#read the file as a single multiline string
$txt = Get-Content -Path 'C:\Users\rseerala\Desktop\ARUN\in.txt' -Raw
# if it contains the magic word '.socket_' followed by a number
if ($txt -match '\.socket_\d+') {
# first split off the 'Added by Manager' stuff
$content, $managerAdded = ($txt -split 'Added by Manager').Trim()
# split the content part into separate blocks of 4 lines
$blocks = $content -split '(\r?\n){2}' | Where-Object { $_ -match '\S' }
# get the index value from the last block
$index = [int]([regex] '(?i)\.socket_(\d+)').Match($blocks[-1]).Groups[1].Value
# now repeat the blocks you already have and output copies with incremented indices
$newBlocks = ($blocks | ForEach-Object {
$_ -replace '_\d+=', ('_{0}=' -f ++$index)
}) -join "`r`n`r`n"
# finally, combine the content part with the new blocks
# and the 'Added by Manager' lines with double newlines
$result = $content, $newBlocks, 'Added by Manager', $managerAdded -join "`r`n`r`n"
# output on screen
$result
# write to a new file
$result | Set-Content -Path 'C:\Users\rseerala\Desktop\ARUN\out.txt'
}
else {
Write-Warning "The file does not contain the word '.socket_' followed by a number.."
}
Output:
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
communication.manageraddress_9=xxx.com
communication.managerid_9=xxx
communication.managerport_9=xxx
communication.socket_9=plain
communication.manageraddress_10=xxx.com
communication.managerid_10=xxx
communication.managerport_10=xxx
communication.socket_10=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2

Powershell - Read, Alter Line, Write new line over old in Text Document

I am reading in line by line of a text file. If I see a specific string, I locate the first and last of a specific character, use two substrings to create a smaller string, then replace the line in the text file.
The difficult part: I have the line of text stored in a variable, but cannot figure out how to write this new line over the old line in the text document.
Excuse the crude code - I have been testing things and only started playing with PowerShell a few hours ago.
foreach($line in [System.IO.File]::ReadLines("C:\BatchPractice\test.txt"))
{
Write-Output $line
if ($line.Contains("dsa")) {
Write-Output "TRUEEEEE"
}
$positionF = $line.IndexOf("\")+1
$positionL = $line.LastIndexOf("\")+1
$lengthT = $line.Length
Write-Output ($positionF)
Write-Output $positionL
Write-Output $lengthT
if($line.Contains("\")){
Write-Output "Start"
$combine = $line.Substring(0,$positionF-1) + $line.Substring($postionL,($lengthT-$positionL))
Write-Output $combine
$line1 = $line.Substring(0,$positionF-1)
$line2 = $line.Substring($positionL,($lengthT-$positionL))
$combined = $line1 + $line2
Write-Output $combined
Write-Output "Close"
}
}```
You can save the file as arrays in Get-Content and Set-Content:
$file=(Get-Content "C:\BatchPractice\test.txt")
Then you can edit it like arrays:
$file[LINE_NUMBER]="New line"
Where LINE_NUMBER is the line number starting from 0.
And then overwrite to file:
$file|Set-Content "C:\BatchPractice\test.txt"
You can implement this in code. Create a variable $i=0 and increment it at the end of loop. Here $i will be the line number at each iteration.
HTH
Based on your code it seems you want to take any line that contains 'dsa' and remove the contents after the first backslash up until the last backslash. If that's the case I'd recommend simplifying your code with regex. First I made a sample file since none was provided.
$tempfile = New-TemporaryFile
#'
abc\def\ghi\jkl
abc\dsa\ghi\jkl
zyx\vut\dsa\srq
zyx\vut\srq\pon
'# | Set-Content $tempfile -Encoding UTF8
Now we will read in all lines (unless this is a massive file)
$text = Get-Content $tempfile -Encoding UTF8
Next we'll make a regex object with the pattern we want to replace. The double backslash is to escape the backslash since it has meaning to regex.
$regex = [regex]'(?<=.+\\).+\\'
Now we will loop over every line, if it has dsa in it we will run the replace against it, otherwise we will output the line.
$text | ForEach-Object {
if($_.contains('dsa'))
{
$regex.Replace($_,'')
}
else
{
$_
}
} -OutVariable newtext
You'll see the output on the screen but it's also capture in $newtext variable. I recommend ensuring it is the output you are after prior to writing.
abc\def\ghi\jkl
abc\jkl
zyx\srq
zyx\vut\srq\pon
Once confirmed, simply write it back to the file.
$newtext | Set-Content $tempfile -Encoding UTF8
You can obviously combine the steps as well.
$text | ForEach-Object {
if($_.contains('dsa'))
{
$regex.Replace($_,'')
}
else
{
$_
}
} | Set-Content $tempfile -Encoding UTF8

Changing multiple lines in a text file based on a psobject

I'm working on a script which will add some additional informations to a txt file. These informations are stored in a CSV file which looks like this (the data will differs each time the script will launch):
Number;A;B;ValueOfB
FP01340/05/20;0;1;GTU_01,GTU_03
FP01342/05/20;1;0;GTU01
The txt file looks like this (data inside will of course differ each time):
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
2|23|157,91|194,23|36,32
1|1|FP01341/05/20|2020-05-02|2020-05-02|2020-05-02|12,19|14,99|2,80|Some info |2222222|blabla|11-111 something||||
2|23|12,19|14,99|2,80
1|1|FP01342/05/20|2020-05-02|2020-05-02|2020-05-02|525,36|589,64|64,28|bla|222222|blba 36||62030|something||
2|5|213,93|224,63|10,70
2|8|120,34|129,97|9,63
2|23|191,09|235,04|43,95
What I need to do is to find a line which contains 'Number' and then add value 'A' and 'B' from a CSV in a form: |0|1 and then on the first line below, at the end, add 'ValueofB' in a form |AAA_01,AAA_03
So the first two lines should look like this at the end:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
2|23|157,91|194,23|36,32
Rest of lines should not be touched.
I made a script which uses select-string method with context to find what I need to - put that into an object and then add to previously found strings what I need to and put that in to an another object.
My script is as follws:
$csvFile = Import-Csv -Path Somepath\file.csv -Delimiter ";"
$file = "Somepath2\SomeName.txt"
$LinesToChange = #()
$script:LinesToChange = $LinesToChange
$LinesOriginal = #()
$script:LinesOriginal = $LinesOriginal
foreach ($line in $csvFile) {
Select-String -Path $file -Pattern "$($Line.number)" -Encoding default -Context 0, 1 | ForEach-Object {
$1 = $_.Line
$2 = $_.Context.PostContext
}
$ListOrg = [pscustomobject]#{
Line_org = $1
Line_GTU_org = $2
}
$LinesOriginal = $LinesOriginal + $ListOrg
$lineNew = $ListOrg.Line_org | foreach { $_ + "|$($line.A)|$($line.B)" }
$GTUNew = $ListOrg.Line_GTU_org | foreach { $_ + "|$($line.ValueofB)" }
$ListNew = [pscustomobject]#{
Line_new = $lineNew
Line_GTU_new = $GTUNew
Line_org = $ListOrg.Line_org
Line_GTU_org = $ListOrg.Line_GTU_org
}
$LinesToChange = $LinesToChange + $ListNew
}
The output is an object $LinesToChange which have original lines and lines after the change. The issue is I have no idea how to use that to change the txt file. I tried few methods and ended up with file which contains updated lines but all others are doubbled (I tried foreach) or PS is using whole RAM and couldn't finish the job :)
My latest idea is to use something like that:
(Get-Content -Path $file) | ForEach-Object {
$line = $_
$LinesToChange.GetEnumerator() | ForEach-Object {
if ($line -match "$($LinesToChange.Line_org)") {
$line = $line -replace "$($LinesToChange.Line_org)", "$($LinesToChange.Line_new)"
}
if ($line -match "$($LinesToChange.Line_GTU_org)") {
$line = $line -replace "$($LinesToChange.Line_GTU_org)", "$($LinesToChange.Line_GTU_new)"
}
}
} | Set-Content -Path Somehere\newfile.txt
It seemed promising at first, but the variable $line contains all lines and as such it can't find the match.
Also I need to be sure that the second line will be directly below the first one (it is unlikely but it can be a case that there will be two or more lines with the same data while the "number" from CSV file is unique) so preferably while changing the txt file it would be needed to find a match for a two-liner; in short:
find this two lines:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
change them to:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
Do that for all lines in a $LinesToChange
Any help will be much appreciated!
Greetings!
Some strange text file you have there, but anyway, this should do it:
# read in the text file as string array
$txt = Get-Content -Path '<PathToTheTextFile>'
$csv = Import-Csv -Path '<PathToTheCSVFile>' -Delimiter ';'
# loop through the items (rows) in the CSV and find matching lines in the text array
foreach ($item in $csv) {
$match = $txt | Select-String -Pattern ('|{0}|' -f $item.Number) -SimpleMatch
if ($match) {
# update the matching text line (array indices count from 0, so we do -1)
$txt[$match.LineNumber -1] += ('|{0}|{1}' -f $item.A, $item.B)
# update the line following
$txt[$match.LineNumber] += ('|{0}' -f $item.ValueOfB)
}
}
# show updated text on screen
$txt
# save updated text to file
$txt | Set-Content -Path 'Somehere\newfile.txt'

How to copy only sentences between two headings

I have a text file in which it has lots of headings and a few sentences below it.
I have wanted to search for the heading and if the heading is available I want to copy sentences below the heading till next heading.
Is it possible in PowerShell please help me I tried
$linenumber= Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" | select-string $search
Select-String $string $dataRead -Context 1, $linenumber| % { $_.Context.PostContext } | out-file "C:\Users\KSYEDSU\Documents\temp\Results.txt"
But it is throwing an error telling it is expecting interger
$linenumber= Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" | select-string $search
Select-String $string $dataRead -Context 1, $linenumber| % { $_.Context.PostContext } | out-file "C:\Users\KSYEDSU\Documents\temp\Results.txt"
ex:
Heading A
1234
34545
13213
Heading B
So I will search for Heading A and if it is available then start copying from 1234... till 13213.
Select-String will find the string inside your text but does not return the position as int. You could loop trough your file and look manueally for the heading and collect the data between.
#Get file content as string array
[System.String[]]$FileContent = Get-Content -Path 'C:\Users\KSYEDSU\Documents\temp\4491309.txt'
#For each line in the file
for ($i = 0; $i -lt $FileContent.Count; $i ++)
{
#If the line equals your start header
if ($FileContent[$i] -eq 'Heading A')
{
$i ++ #Get the next line
#Return line until end header appears
while ($FileContent[$i] -ne 'Heading B')
{
$FileContent[$i] #Return line
$i ++ #Get next line
}
}
}
You could use regex to do this, so you don't have to loop through all lines in the text file:
$headingStart = 'Heading A'
$headingEnd = 'Heading B'
# get the file content in one string, including all newline characters
$content = Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" -Raw
# since the real headings may contain characters that have special meaning in regex, we escape them
$regex = '(?s)^{0}\s+(.*)\s{1}' -f [regex]::Escape($headingStart), [regex]::Escape($headingEnd)
if ($content -match $regex) {
$matches[1] | Out-File -FilePath "C:\Users\KSYEDSU\Documents\temp\Results.txt"
}
else {
Write-Host "No text found between $headingStart and $headingEnd"
}
Using your example, the resulting file will contain:
1234
34545
13213
You can use a switch statement combined with the -Regex and -File flags.
$insideHeaders = $false
. {switch -Regex -File "C:\Users\KSYEDSU\Documents\temp\4491309.txt" {
'Heading A' { $insideHeaders = $true }
'Heading B' { return }
default {
if ($insideHeaders) { $_ }
}
}} | Out-File "C:\Users\KSYEDSU\Documents\temp\Results.txt"
Explanation:
Each value between single quotes is a regex string. You will have to backslash (\) escape any special regex characters, which can be done automatically using [regex]::Escape(string).
When the bottom header (Heading B in this case) is reached, the return statement will exit the switch statement.
All lines not matching one of the headers will trigger the default condition. The default condition will only output a line if the first header has been found.

Replace the first occurence of a string in a file

In a PowerShell script, to replace the first occurrence of a string in a file I came with the code below, which keeps track in a variable whether the replacement was made.
Is there a more elegant (idiomatic) way of doing this?
$original_file = 'pom.xml'
$destination_file = 'pom.xml.new'
$done = $false
(Get-Content $original_file) | Foreach-Object {
$done
if ($done) {
$_
} else {
$result = $_ -replace '<version>6.1.26.p1</version>', '<version>6.1.26.p1-SNAPSHOT</version>'
if ($result -ne $_) {
$done = $true
}
$result
}
} | Set-Content $destination_file
So let's say that you had a file named Test.txt and it's contents were:
one
two
four
four
five
six
seven
eight
nine
ten
And you want to change just the first instance of four to be three instead:
$re = [regex]'four'
$re.Replace([string]::Join("`n", (gc C:\Path\To\test.txt)), 'three', 1)
If it is xml, handle it as xml:
$xml = [xml](gc $original_file)
$xml.SelectSingleNode("//version")."#text" = "6.1.26.p1-SNAPSHOT"
$xml.Save($destination_file)
SelectSingleNode will select the first version element. Then replace it's inner content and save to the new file. Add a check for the inner content being 6.1.26.p1 if you want to specifically replace only that.