Ignore Tokens on the Front of a Regular Expression in Powershell - powershell

Hi so I have read over regular expressions and all that but don't really fully understand it. Just looking for a little help here after a lot of searching on here and google.
I have an XML file that I am editing but for now let's pretend I'm doing just a single string. This works great except that I lose Connection Database="SQLEventLog" text in the replace. What kind of ignore token do I use here?
Here is my code
$passedString = '<Connection Database="SQLEventLog" >Data
Source=;Initial Catalog=Connector;Integrated Security=True</Connection>'
search($passedString)
function search ($string)
{
$pattern = '*Data Source=*'
if ($string -like '*Data Source=*')
{
Write-Warning 'found'
$string = $string -replace '.*Data Source=*', 'Data
Source=localhost'
}
Write-Warning $string
}

So, a few things. Best-practice for defining function parameters is to use the Param() clause. Functions in PowerShell are not called with parenthesis, but are separated by spaces (e.g. Function arg1 arg2 Arrayarg3,Arrayarg3)
Additionally, the -like comparison operator does not use regex, it's a wildcard comparison. I've updated your example to accomplish your goal.
Function Search
{
Param($String)
If ($String -like '*Data Source=*')
{
Write-Warning 'found'
$string = $string -replace 'Data\sSource=', 'Data Source=localhost'
}
Write-Warning $string
}
$passedString = 'Data Source=;Initial Catalog=Connector;Integrated Security=True'
Search $passedString
Note: the -replace function DOES use regex for the first piece.

Related

powershell How to capture 'left side' of the -replace regex?

First time posting.
i have the following code to replace the suffix of an email and its working fine
replace all characters after # sign with #testdomain.com
$a = 'john.doe#domain.com'
$b = $a -replace "[?=#].*", '#testdomain.com'
$b
john.doe#testdomain.com
what i would like to do, is to capture the actual left side 'source' regex expression to a variable, which would be #domain.com so that i know what i;m replacing and i don;t know how to do it.
Sorry if this had been posted before.
Thank you
So, I'm not sure if this is possible using only the -replace operator and without the use of -match which would store the capture group on the $Matches automatic variable.
This is how you could do it using the regex class directly:
$a = 'john.doe#domain.com'
$Capture = #{}
$b = [regex]::Replace($a, "[?=#].*", {
param($s)
'#testdomain.com'
$Capture.Value = $s.Value
})
$b # => john.doe#testdomain.com
$Capture.Value # => #domain.com
This what I could think using only -replace, adding a delimiter (// in this case) to the replaced string followed by the capture group $0 and then splitting the replacement. Though, this is obviously not a robust solution.
$a = 'john.doe#domain.com'
$b, $capture = $a -replace "[?=#].*", '#testdomain.com//$0' -split '//'
$b # => john.doe#testdomain.com
$capture # => #domain.com
To change the user part you can replace ^.*(?=#):
PS ~> $a = 'john.doe#domain.com'
PS ~> $a -replace '^.*(?=#)', 'jane.doe'
jane.doe#domain.com
The (?=#) construct is known as a lookahead, and describes a zero-length string at the position immediately before the #.

PowersShell UpperCase Replace

Ok... after 99 different combinations...
I want to replace in thousands of files all occurences of EnumMessage.something to EnumMessage.SOMETHING so uppercase the second word. Which may be standalone or followed by a dot or followed by a (
$output = 'EnumMessage.test(something) and EnumMessage.Tezt andz EnumMessage.ALREAdY. ' -creplace 'EnumMessage\.(\w+)', 'EnumMessage.$1.ToUpper()'
$output
So the above places the function Upper there (the word) but it does not upper the second word.
In PowerShell 6 and later, the -replace operator also accepts a script block that performs the replacement. The script block runs once for every match.
In PowerShell 5, apply the Regex.Replace Method.
$string = 'EnumMessage.test(something) and EnumMessage.Tezt andz EnumMessage.ALREAdY. '
$pattern = '(?<=EnumMessage\.)(\w+)'
# (?<=EnumMessage\.) = positive lookbehind
if ( $PSVersionTable.PSVersion.Major -ge 6 ) {
$string -replace $pattern, { $_.Value.ToUpper() }
} else {
[regex]::Replace( $string, $pattern, { $args.Value.ToUpper() } )
}
There are definitely some challenges. I'm really not the best at RegEx. Any time I tried to leverage the $matches collection I was only able to replace the first match. There's probably something I'm forgetting about that functionality. However, I was able to cook up the below:
[RegEx]::Matches($String, '(?<=EnumMessage\.)\w+') |
ForEach-Object{
$Replace = $String.Substring($_.Index, $_.Length).ToUpper()
$String = $String.Remove($_.Index, $_.Length)
$String = $String.Insert($_.Index, $Replace)
}
$String
Note: I used a RegEx lookbehind, but I'm not positive that had much to do with the outcome.
The .Net [RegEx] class returned objects with the location of the matches in the string so I used that to strategically remove then add the ucased strings. Which should return: EnumMessage.TEST(something) and EnumMessage.TEZT andz EnumMessage.ALREADY.

Powershell handle variable with special character (open bracket)

I have a script where two variables are compared, it can happen that a variable contains open brackets without closing, like in the example. Then a System.ArgumentException occur: "...not enough closing brackets.."
$test1="Testtext"
$test2="Testtext (3x(2x0,25"
if(!($test1 -match $test2)){ "test"}
how can i deal with it?
-match performs regular expression matching - use Regex.Escape() to automatically escape any escapable sequence in a verbatim pattern string:
$text = 'Text with (parens)'
$pattern = '(par'
if($text -match [regex]::Escape($pattern)){
"It worked!"
}

Aesthetically Pleasing String Replace Statement

I currently have code that looks like this:
$onestr = $twostr -replace "one","two" -replace "uno","dos"
I would like to format it like this:
$onestr = $twostr -replace "one","two"
-replace "uno","dos"
such that the replace statements stack on top of each other.
I could use backtic as the line continuation character, but other stackoverflow questions cover why that is not a good idea.
I tried code that looks like this:
$onestr = ($twostr -replace "one","two"
-replace "uno","dos"
)
But I got an error that the paren is not matched.
My actual code has several replace statements (not just two).
If you have a lot of replacements, what about a different approach which allows much more scope for the aligning of the replacement pairs nicely, without affecting the block of replace code.
$onestr = 'one thing uno thing'
$Pairs = #{
'one' = 'two'
'uno' = 'dos'
}
$Pairs.GetEnumerator() | ForEach-Object {
$onestr = $onestr -replace $_.Name, $_.Value
}
$onestr
This is less common, maybe awful, but more exactly the layout you ask about - using the .Net framework string replace method instead of the PowerShell operator.
$onestr = 'one thing uno thing'
$onestr.
Replace('one', 'two').
Replace('uno', 'dos')
If you don't want to use a backtick ` as a line continuation character, then I suggest you just make these separate statements:
$onestr = $twostr -replace "one","two"
$onestr = $onestr -replace "uno","dos"
An athletically, not aesthetically, pleasing method, more obvious than a backtick, might be a comment:
$onestr = $twostr -replace "one","two" <#
#> -replace "uno","dos" <#
#> -replace "foo","bar"
At least it's DRYer than repeating the assignments!
Or the heavy artillery: .NET Regex.Replace with a scriptblock so that the text is iterated only once:
function translate(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$text,
[Parameter(Mandatory=$true)]
[hashtable]$map,
[switch]$caseSensitive
) {
$options = if ([bool]$caseSensitive) { '' } else { '(?i)' }
([regex]($options +
($map.Keys -replace '[$^*?.+|()\[\]{}\\]', '\$&' -join '|')
)).Replace($text, { param([string]$match) $map[$match] })
}
'Abc' | translate -map #{
'a'='1'
'b'='2'
'c'='3'
}
It's case-INsensitive by default.
You might consider using a metafunction with a [scriptblock] parameter to unwrap your expression into one line without any backticks. For example, a simple Invoke-Unwrap metafunction could be used to define ConvertTo-Spanish as follows:*
function Invoke-Unwrap( [scriptblock]$sb) {
Invoke-Expression ($sb -replace '\r?\n\s*-', ' -')
}
Invoke-Unwrap {
function script:ConvertTo-Spanish( [string]$str ) {
$str
-replace "one","uno"
-replace "two","dos"
-replace "thing","lo"
}
}
ConvertTo-Spanish "thing one and thing two"
As written Invoke-Unwrap is very general, it allows word-wrapping before any operator or switch starting with a "-". It could be expanded to allow other operator-characters at the beginning of a line, permitting arithmetic expressions to be written in a word-wrapped form. I'm thinking ($sb -replace '\r?\n\s*([-+*/])', ' $1').
Or, if you want, you can enhance the processing of the string passed to Invoke-Expression to make your own little domain-specific language, perhaps implementing something more SED like.
Apologies for my limited knowledge of Spanish... :-)

PowerShell: get data between two strings

I've got next data into variable $out (type is Object[]):
Success...
Go!
0:#217> trace && .quit
0x000 Subline : _OK
0x008 Timed : NO
0x016 Check : _OK
0x022 Post :
0x030 Offset : None
0x038 Hint : False
0x050 NextHint : False
quit:
I need extract text between string 0:#217> trace && .quit and quit:
I wrote:
[Regex]::Match($out, "(?<=.quit').+?(?=quit:)").Value
But this extracts required data into a line (type String), not a column (Object[]). How to fix this?
P.S.
I solved the problem by myself as follows
([Regex]'(?is)(?:(?<=\.quit).+(?=quit:))').Match(($out -join "`n")).Value
But maybe there is more perfect way to do this?
Problem solved
([Regex]'\s+0x([^q]+)').Match(($out -join "`n")).Value
Just use the -split function to create a String[] of your result:
$result = ([regex]::Match($a, '\.quit(.*)quit:').Groups[1].value) -split [System.Environment]::NewLine
[Edit: this will work if $out is a String[], e.g. from $out = Get-Content results.txt, from your other comments, you might have something different].
As a general approach, have a true/false flag which chooses whether lines are allowed through or not, and when you see the first line then set the flag, and when you see the last line you want, change the flag.
$middle = foreach ($line in $out) {
if ($line -match '^quit') { $allow = $false }
if ($allow) { write-output $line }
if ($line -match '0:#217>') { $allow = $true }
}
The ordering of the tests determines whether the start or end lines show up in the results or not.
This can be shortened on the console for typing, into something like:
# loop # end line clears flag # print? # start line sets flag
$out |% { if($_ -match '^quit'){$f=0}; if ($f){$_}; if ($_ -match '0:#217>'){$f=1} }
This might work, but it makes some assumptions:
$out -match '^ '
$out is always a String[]. Force it with #($out) if it might be a single string.
You are using PowerShell v4 or v5, so -operator will act as a filter on an array.
Your example data is accurate, and all the lines you want start with a space, and all the other lines do not.
The following is for very simple searches, get the string data between a starting string and ending string.
Upsides
Very simplistic
Downsides
Does poorly for data with multiple matches etc
Code
Function Get-StringBetweenStartEnd {
Param($Text,$Start,$End)
$Regex = [Regex]::new("(?<="+$Start+")(.*)(?="+$End+")")
$Match = $Regex.Match($String)
if($Match.Success) { Return $Match.Value}else{Return ""}
}
Example Usage
$String = "Test: disconnected: 10.10.10.1::59270 (VNC Viewer closed)"
$Result = Get-StringBetweenStartEnd -Text $String -Start "nected:" -End "::"
$Result.Trim()
Output:
10.10.10.1