Edit text between two lines using powershell - 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.

Related

Extract string between two special characters in powershell

I need to extract a list with strings that are between two special characters (= and ;).
Below is an example of the file with line types and the needed strings in bold.
File is a quite big one, type is xml.
<type="string">data source=**HOL4624**;integrated sec>
<type="string">data source=**HOL4625**;integrated sec>
I managed to find the lines matching “data source=”, but how to get the name after?
Used code is below.
Get-content regsrvr.txt | select-string -pattern "data source="
Thank you very much!
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4625;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
The XML is not valid, so it's not a clean parse, anyway you can use string split with regex match:
$html = #"
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4625;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
"#
$html -split '\n' | % {$null = $_ -match 'data source=.*?;';$Matches[0]} |
% {($_ -split '=')[1] -replace ';'}
HOL4624
HOL4625
Since the connectionstring is for SQL Server, let's use .Net's SqlConnectionStringBuilder to do all the work for us. Like so,
# Test data, XML extraction is left as an exercise
$str = 'data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096'
$builder = new-object System.Data.SqlClient.SqlConnectionStringBuilder($str)
# Check some parameters
$builder.DataSource
HOL4624
$builder.IntegratedSecurity
True
You can expand your try at using Select-String with a better use of regex. Also, you don't need to use Get-Content first. Instead you can use the -Path parameter of Select-String.
The following Code will read the given file and return the value between the = and ;:
(Select-String -Path "regsrvr.txt" -pattern "(?:data source=)(.*?)(?:;)").Matches | % {$_.groups[1].Value}
Pattern Explanation (RegEx):
You can use -pattern to capture an String given a matching RegEx. The Regex can be describe as such:
(?: opens an non-capturing Group
data source= matches the charactes data source=
) closes the non-capturing Group
(.*?) matches any amount of characters and saves them in a Group. The ? is the lazy operator. This will stop the matching part at the first occurence of the following group (in this case the ;).
(?:;) is the final non-capturing Group for the closing ;
Structuring the Output
Select-String returns a Microsoft.PowerShell.Commands.MatchInfo-Object.
You can find the matched Strings (the whole String and all captured groups) in there. We can also loop through this Output and return the Value of the captured Groups: | % {$_.groups[1].Value}
% is just an Alias for For-Each.
For more Informations look at the Select-String-Documentation and try your luck with some RegEx.

Powershell replace between start and end

I need to replace everything between two points.
$import = Get-Content C:\bookmarks.html
$newbody = Get-Content C:\newbookmarks.html
$remove = '(?<=<DT><H3 ADD_DATE=""1544626193"" LAST_MODIFIED=""154649885"">Import-IE</H3>).*?(?=</DL>)'
$import | %{$_.replace($remove,"$newbody")}
My problem is to get all content between start:
<DT><H3 ADD_DATE=""1544626193"" LAST_MODIFIED=""154649885"">Import-IE</H3>
and the end:
</DL>
incl multiple lines
Example html:
<DT><H3 ADD_DATE="1544626193" LAST_MODIFIED="1546498855">Import-IE</H3>
<DL><p>
<DT>golem.de
<DT>heise online
</DL>
Regards
A couple of changes needed to make this work:
One big multiline string
Since you want to do a replace over multiple lines, we need to makes sure all the lines are contained in the same string, so let's start with that - we can use the -Raw parameter switch with Get-Content:
$import = Get-Content C:\bookmarks.html -Raw
Exact pattern matching in regex
Next up we have the regex pattern itself - there's a few discrepancies between that and the sample content you've shown:
LAST_MODIFIED=""154649885"" # pattern has nested double-quotes and only one 5 at the end
LAST_MODIFIED="1546498855" # input uses just one pair of double-quotes and value has two 5's at the end
So let's fix that, and make sure the input string we're looking for is properly escaped while we're at it:
$remove = "(?<=$([regex]::Escape('<DT><H3 ADD_DATE="1544626193" LAST_MODIFIED="1546498855">Import-IE</H3>'))).*?(?=</DL>)"
String.Replace doesn't support regex
Then, we'll have to abandon the String.Replace() method that you're currently using - because it doesn't actually support regex - so we'll use the -replace operator instead:
$import -replace $remove,"$newbody"
Use -replace in SingleLine mode
The only thing we need now, is to instruct the regex parser to treat the input in SingleLine mode - so that .*? will capture newlines as well. This is super easy though, we just add an options flag s at the start of the regex pattern:
$import -replace "(?s)$remove","$newbody"
And that's it :)
$import = Get-Content C:\bookmarks.html -Raw
$newbody = Get-Content C:\newbookmarks.html
$remove = "(?<=$([regex]::Escape('<DT><H3 ADD_DATE="1544626193" LAST_MODIFIED="1546498855">Import-IE</H3>'))).*?(?=</DL>)"
$import -replace "(?s)$remove","$newbody"

powershell non greedy replace upto the pattern

I have a file with multiple lines as shown in the example below, but I need the chars mentioned in the pattern to be present in the output, in other words non-greedy match.
I need a way to remove the first part of the line and get the line which contains with starting with '07' or '08' and everything in the line till end of line.
Example:
Input: 'SELECT TZ_OFFSET (SESSIONTIMEZONE) FROM DUAL07.16.0162 (B0375, U0847)'
output: 07.16.0162 (B0375, U0207)
Input: 'SELECT TZ_OFFSET (SESSIONTIMEZONE) FROM DUAL08.15.0162 (B03075, U07)'
output: 08.15.0162 (B08075, U07)
This should work:
$str = 'SELECT TZ_OFFSET (SESSIONTIMEZONE) FROM DUAL07.16.0162 (B0375, U0847)'
$str -replace '^.*?DUAL(.*)$','$1'
New-Variable -Name file -Value "c:\abc.txt" #This file contains the line that needs to be edited
(Get-Content -Path $file) -replace '^.*?DUAL(.*)$','$1'|set-content $file

Powershell's -replace() does not preserve line breaks, or I'm doing it wrong

I have a text file with some lines of text and I want to insert into another text file.
aaa.txt:
aaaaaaaaaa
bbbbbbbbb
ccccccccc
dddddddd
eeeeeeee
bbb.txt:
slkdfjlskdfj dlfjsldkfj slkdfjs
{{replace}}
sdlkfjslkfj sldkfjsld kfjsldk fjsldk f
sldkfjslkfjlskjflskdjf
sdkfjslkjflsklsdjkf
sldfkjslkfjlskfj
But when I replace {{replace}} with the contents of aaa.txt it puts all the text on one line- I want to preserve the line breaks from aaa.txt:
PS> $bbb = cat .\bbb.txt
PS> $bbb -replace('{{replace}}',(cat .\aaa.txt))
slkdfjlskdfj dlfjsldkfj slkdfjs
aaaaaaaaaa bbbbbbbbb ccccccccc dddddddd eeeeeeee
sdlkfjslkfj sldkfjsld kfjsldk fjsldk f
sldkfjslkfjlskjflskdjf
sdkfjslkjflsklsdjkf
sldfkjslkfjlskfj
-replace replaces individual strings. cat .\aaa.txt returns an array of strings, which -replace then has to convert to a single string before -replace can do something with it -- hence your result. In PowerShell v3, the -raw parameter was added to Get-Content to circumvent this behavior, so if you have that, it's as simple as this:
$bbb -replace '{{replace}}', (cat -raw .\aaa.txt)
This seems to work
($bbb -replace('{{replace}}',$aaa)) -replace " ","`n`r"

Change specific part of a string

I've got a .txt-File with some text in it:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"*******"
So now, is there a way to replace those *'s with e.g. numbers or sth. ?
If so, may you tell me how to do it?
Much like Rahul I would use RegEx as well. Considering the application I'd run Get-Content through a ForEach loop, and replace text as needed on a line-by-line basis.
Get-Content C:\Path\To\File.txt | ForEach{$_ -replace "(PKG_DOMAINJOIN_PASSWD;`")([^`"]+?)(`")", "`${1}12345678`$3"}
That would output:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678"
On second thought, I don't know if I'd do that. I might import it as a CSV, update the property, and export the CSV again.
Import-CSV C:\Path\To\File.txt -Delimiter ";" |%{if($_.Property -eq "PKG_DOMAINJOIN_PASSWD"){$_.value = "12345678";$_}else{$_}|export-csv c:\path\to\newfile.txt -delimiter ";" -notype
If You are using Powershell V2.0 (Hopefully) you can try something like below. gc is short hand for get-content commandlet.
(gc D:\SO_Test\test.txt) -replace '\*+','12345678'
With this the resultant data would be as below (notice the last line)
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
<Rest of the lines here>
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678" <-- Notice here; *'s changed to numbers
Rahul's answer was good, I just wanted to mention that *+ will replace all instances of a single * character or more, so it would match any other place there is at least one star. If what you posted is all you would ever expect for you sample data though this would be fine.
You could alter the regex match to make it more specific if it was needed by changing it to something like
\*{3,0}
which would match 3 or more stars, or very specific would be
(?<=")\*{3,}(?=")
which would replace 3 or more stars which are surrounded by double quotes.
Here's a function that uses regex lookahead and lookbehind zero-length assertions to replace named parameters in a string similar to your example:
function replace-x( $string, $name, $value ) {
$regex = "(?<=$([regex]::Escape($name));`").*(?=`")"
$string -replace $regex, $value
}
Its reusable for different settings in your file, e.g:
$settings = get-content $filename
$settings = replace-x $settings PKG_DOMAINJOIN_USER foo
$settings = replace-x $settings PKG_DOMAINJOIN_PASSWD bar