Powershell: Find and replace words split by newline - powershell

So I have a text file that looks something like this:
Members : {USER\member1, USER\member2, US
ER\member3, USER\member4, USER
\member5, USER\member6}
and I would like to remove USER\. The following code removes it but not when it's split by a newline, for example when US on one line and ER\ on another line.
Foreach-Object { %{$_.Replace('USER\', '') }
Putting `n or `r in there doesn't work. Any help is appreciated.

Try this:
PS > ((Get-Content .\t.txt) | % { $_.Trim() }) -join "" -replace "USER\\"
Members : {member1, member2, member3, member4, member5, member6}
If the text is in a string-array, switch out (Get-Content .\t.txt) with your variable. If you have the text in a string(not array) variable, use:
($MYSTRINGVAR.Split("`r`n") | % { $_.Trim() }) -join "" -replace "USER\\"
EDIT Just modify the "Members" part:
$text = (Get-Content .\input.txt) -join "`r`n"
($text | Select-String '(?s)(?<=Members : \{)(.+?)(?=\})' -AllMatches).Matches | % {
$text = $text.Replace($_.Value, ($_.Value -split "`r`n" | % { $_.Trim() }) -join "" -replace "USER\\")
}
$text | Set-Content output.txt

There are probably easier ways to get there, but you can give this one a try:
$Text = #'
Members : {USER\member1, USER\member2, US
ER\member3, USER\member4, USER
\member5, USER\member6}
'#
# First - USER\ with newline inside..
foreach ($index in 1..4) {
$Text = $Text -replace ('USER\\'.Insert($index,'(\r\n\s+)')), '$1'
}
# Than - pure USER\
$Text = $Text -replace 'USER\\'
$Text
As you can see I create few patterns that contain and keep that element in results (, '$1'). For simple ones - I just remove USER\
I've used herestring to create text to work with, it's possible that \r may not be needed for actual file.

This is actually just a comment to Graimer's solution, but it would've been too long and also not readable enough as a comment, so I'm making it a (supplementary) answer instead.
To re-wrap the string after removing USER\ you could do something like this:
$s = "Members : {member1, member2, member3, member4, member5, member6}"
$s -match '^(.*?{)(.*)(})$'
$pad = " " * $matches[1].Length
$matches[1] + ($matches[2] -replace '(.{1,20},) ', "`$1`r`n$pad") + $matches[3]
The first regular expression splits the string into 3 parts that can be accessed via the $matches collection:
Name Value
---- -----
3 }
2 member1, member2, member3, member4, member5, member6
1 Members : {
0 Members : {member1, member2, member3, member4, member5, member6}
$matches[1] is the prologue including the opening curly bracket, $matches[2] is the member list, and $matches[3] is the closing curly bracket. Now you only need to wrap $matches[2] at whatever length you want:
'(.*{1,20},) '
The above means "longest match of at most 20 characters followed by a comma and a space". Replace that with the 1st group ($1) followed by a line-break (```rn``) and a number of spaces that matches the length of the prologue ($pad`) and glue it back together with prologue and trailing curly bracket.

Related

Print the lines from a file which contains words given by the user

I want to type in words and the script prints the lines which contains the words.
I have the following data in the folder foods.txt
1;400;100000;pizza tea
11;178;56124;coke hamburger
7;777;20000;sprite pizza
10;150;100000;coke sandwich fries
So for example if I type in pizza, it prints out the first and the 3rd line:
1;400;100000;pizza tea
7;777;20000;sprite pizza
My script can filter for one word, but I don't know how to make it to filter for all the given words so if I type in: tea fries, it should print out the first and last line.
I was thinking of filtering the main file, then redirect it to another file, filter that file too, or something like that?
$word = Read-Host "Type in the words"
Copy-Item foods.txt first.txt
foreach ($i in Get-ChildItem *first.txt) {
$filtered = Get-Content "foods.txt" | % {
if ($_ -match "$word") {
Write-Host $_
}
}
$filtered >> second.txt
Copy-Item second.txt first.txt
}
Get-Content second.txt
Remove-Item first.txt
Remove-Item first.txt
Your foods.txt file looks remarkably like a CSV file without headers to me..
This means you could also use the CSV parsing method to go about this:
# Import the file as CSV to get an array of objects.
# I'm just making up the headers here..
$foods = Import-Csv -Path 'foods.txt' -Delimiter ';' -Header 'ItemsSoldToday','ItemsSoldThisWeek','InStock','Description'
# Next, read the words typed in by the user, split on whitespace character(s)
# and escape any characters that might have special meaning in a regular expression
$words = (Read-Host "Type in the words separated by a space character") -split '\s+' | ForEach-Object { [regex]::Escape($_) }
# Join these words together with a pipe symbol "|" that will make an 'OR' within the regex match
# and filter the objects that have any of these words in the 'Description' field
$chosen = $foods | Where-Object { $_.Description -match ($words -join '|') }
# Example: when the user types "tea fries", $chosen now contains an array of objects:
#
# ItemsSoldToday ItemsSoldThisWeek InStock Description
# -------------- ----------------- ------- -----------
# 1 400 100000 pizza tea
# 10 150 100000 coke sandwich fries
# If you want the returned output to be exactly like the input text file, simply recombine the values
$chosen | ForEach-Object { $_.PSObject.Properties.Value -join ';' }
This will return:
1;400;100000;pizza tea
10;150;100000;coke sandwich fries
To filter by a list of words entered via Read-Host you need to split that input and build a regular expression from the tokens:
$words = Read-Host '...'
$re = ($words.Split() | Where-Object {$_} | ForEach-Object {[regex]::Escape($_)}) -join '|'
If your words don't contain characters that have a special meaning in regular expressions (like dots or square brackets), or you want them handled as regular expressions anyway, you can omit the step | ForEach-Object {[regex]::Escape($_)}.
Also, PowerShell comparison operators work as enumerators, so you can use them directly on arrays:
(Get-Content 'foods.txt') -match $re | Set-Content 'result.txt'

Swap last.first to first.last in text file

I am trying to swap two words in a string. I currently have a txt file with a column of users formatted last.first. How can I swap that to first.last?
-split the string and concatenate:
$Last,$First = "Lastname.Firstname" -split '\.'
$newString = "$First.$Last"
or use -replace to reorder the two:
"Lastname.Firstname" -replace '(\w+)\.(\w+)','$2.$1'
gc .\names.txt |% { "{1}.{0}" -f $_.split('.') }
Get the lines out of the file with gc which is an alias for Get-Content
Loop over them with % which is an alias for ForEach-Object
Split() each line around the full stop, into an array of two items
Use the "" -f string formatting operator to build a string taking array items in the order 1, 0 which swaps the order of the parts.
Quick and dirty - minimal error checking...
Get-Content .\test.txt |
ForEach-Object {
if ( $_.Contains('.') ) {
$_.Split('.')[1] + '.' + $_.Split('.')[0] }
else { $_ }
}

Keep DC part of distinguished name

I have for input distinguished names like the following:
CN=A00.user,OU=MyOU,OU=A00,OU=MyOU3,DC=my,DC=domain
CN=A01.user1,OU=MyOU1,OU-MyOU2,OU=A00,OU=MyOU3,DC=my,DC=first,DC=domain
I need to print only the DC part, to get an output like:
my.domain
my.first.domain
Looks like split or replace should work, but I'm having trouble figuring out the syntax.
You can use Get-ADPathname.ps1 with the -Split parameter, Select-String with a regular expression, and the -join operator:
(
Get-ADPathname 'CN=A01.user1,OU=MyOU1,OU-MyOU2,OU=A00,OU=MyOU3,DC=my,DC=first,DC=domain' -Split | Select-String '^DC=(.+)' | ForEach-Object {
$_.Matches[0].Groups[1].Value
}
) -join '.'
Output:
my.first.domain
Here's a quick and dirty way to get it done.
("CN=A00.user,OU=MyOU,OU=A00,OU=MyOU3,DC=my,DC=domain " -split "," |
Where-Object { $_.StartsWith("DC=") } |
ForEach-Object { $_.Replace("DC=","")}) -join "."
Produces
my.domain
I would simply remove everything up to and including the first ,DC= and then replace the remaining ,DC= with dots.
$dn = 'CN=A00.user,OU=MyOU,OU=A00,OU=MyOU3,DC=my,DC=domain',
'CN=A01.user1,OU=MyOU1,OU-MyOU2,OU=A00,OU=MyOU3,DC=my,DC=first,DC=domain'
$dn -replace '^.*?,dc=' -replace ',dc=', '.'

Powershell search through two lines

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.

Problems with replacing newline

Iam trying to replace following string with PowerShell:
...
("
Intel(R) Network Connections 14.2.100.0
","
14.2.100.0
")
...
The code that I use is:
Get-Content $logfilepath |
Foreach-Object { $_ -replace '`r`n`r`n', 'xx'} |
Set-Content $logfilepath_new
But I have no success, can someone say me, where the error is?
First, you are using single quotes in the replace string -
'`r`n`r`n'
that means they are treated verbatim and not as newline characters, so you have to use -
"`r`n`r`n"
To replace, read the file as string and use the Replace() method
$content=[string] $template= [System.IO.File]::ReadAllText("test.txt")
$content.Replace("`r`n`r`n","xx")
Get-content returns an array of lines, so CRLF is essentially your delimiter. Two CRLF sequences back to back would be interpreted as the end of the currrent line, followed by a null line, so no line (object) should contain '`r`n`r`n'. A multi-line regex replace would probably be a better choice.
as alternate method using PS cmdlets:
Get-Content $logfilepath |
Foreach-Object -Begin { $content="" } -Process { $content += $_ ; $content += "xx" } -End { $content } |
Set-Content $logfilepath_new
I used the following code to replace somestring with newline:
$nl = [System.Environment]::NewLine
$content = $content.Replace( somestring, $nl )