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

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 #.

Related

Extract unspecified length of characters to the right of a pattern - Powershell

I have a function that returns a string value:
"D:\Put_Your_Temporary_Files_HERE\Auto_Receive\user_out-automation.ini:9:IpAccessListEx=1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|"
Everything after the equals sign can change depending on what's return in my function. What I want to do is return all text after the equals (=) sign.
Any help will be greatly appreciated.
Thanks
Rob
You can use Select-String Cmdlet with -Pattern parameter to specify the text to find on input
$r = "IPAddressEx=|2.33.31.45|108.38.48.17|" | Select-String -Pattern "IPAddressEx=(.*)"
It return Microsoft.PowerShell.Commands.MatchInfo object on successful pattern match. So to get the sub group from match you have to the access Matches property.
$r.Matches.Groups[1].Value
You can do a simple -split like
$str = "D:\Put_Your_Temporary_Files_HERE\Auto_Receive\user_out-automation.ini:9:IpAccessListEx=1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|"
($str -split '=', 2)[-1]
Result: 1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|
OR use -replace:
$str = "D:\Put_Your_Temporary_Files_HERE\Auto_Receive\user_out-automation.ini:9:IpAccessListEx=1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|"
$str -replace '.*=(.+)$', '$1'
Result: 1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|
OR use the regex .Match() method
$str = "D:\Put_Your_Temporary_Files_HERE\Auto_Receive\user_out-automation.ini:9:IpAccessListEx=1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|"
([regex]'.*=(.+)$').Match($str).Groups[1].Value
Result: 1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|
OR even the String methods IndexOf() combined with SubString():
$str = "D:\Put_Your_Temporary_Files_HERE\Auto_Receive\user_out-automation.ini:9:IpAccessListEx=1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|"
$str.Substring($str.IndexOf("=") + 1)
Although this last alternative is not advisable because IndexOf() may return -1 if the search string is not found
An alternative to Abdul's answer is to use regular expressions. For your case, this should work:
$ret = [regex]::match($string,'(?<=.*=).*')
if ($ret.Success) {
$tag = $ret.Value
}
The regex uses a positive lookbehind, which finds all characters which precede an '='. It then finds all characters which follow it. It stores the object in $ret. You can get your value with $ret.Value.
This returns 1|131.203.181.66|1|10.21.5.34|1|109.146.13.135|1|10.21.3.3|

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 -Replace appending (oddly) instead of replacing

EDIT: PowerShell version is 5.1
I am writing some code that will take the value of a variable from a file, and if that string is made up of other variables within the file, it will locate them and replace it to reconstruct the run-time value. It assumes the variable contains a string, and that the string describes a directory.
For example, the file contains:
$var0 = "C:\Users\v-anad\Documents"
$var1 = "$var0\TestFolder"
Then when the code looks for $var1, it should return something like: "C:\Users\v-anad\Documents\TestFolder\"
However, the actual output I see is:
\TestFolder"-anad\Documents"
When it replaces, it deletes the correct substring ($var0), but when it inserts the value of $var0, it skips over the characters that existed there before, and appends the remaining characters to the end of the string. I have no clue what/where I've gone wrong.
Here is the code in question:
function Get-Var-Value-In-File([string]$varName, [string]$file) {
$regex = "(?<=\$varName = )[^`n]*"
$content = Get-Content -Raw $file
return [regex]::Match($content, $regex).Value
}
$file = 'C:\Users\v-anad\Documents\TestFolder\TestVars.ps1'
$var = '$var1'
$value = Get-Var-Value-In-File $var $file
$regex = "\$[^\\]*"
$nextVar = [regex]::Match($value, $regex).Value
$nextValue = Get-Var-Value-In-File $nextVar $file
Write-Output "$var = $value"
Write-Output "$nextVar = $nextValue"
Write-Output $nextVar.Replace($nextVar, $nextValue)
Write-Output ($value -replace [regex]::Escape($nextVar),$nextValue)
Output:
$var1 = "$var0\TestFolder"
$var0 = "C:\Users\v-anad\Documents"
"C:\Users\v-anad\Documents"
\TestFolder"-anad\Documents"
Note how the code above does not account for the extra quotes that would be inserted into the final value, so should this curious behavior be fixed, the output will be: ""C:\Users\v-anad\Documents"\TestFolder\"
The problem was there was a carriage return (\r, or `r in PowerShell) that my regex included in the match, causing the behavior when the string replacement occurred. Thanks to PerSerAl for being a second pair of eyes to catch it.

Remove everything from string before given variable

I have a string some\string/with/**/special\chars\haha. In variable I hold chars string and I try to remove everything before and including chars so expected output would be \haha
I tried sth like:
$temp = "some\string/with/**/special\chars\haha"
$tmp="chars"
$temp -replace '(?s)^.*$tmp', ''
and
$temp -replace '(?s)^.*$([regex]::Escape($tmp))', ''
but the only thing that works is when I put the string directly into regex condition. Only this example gives expected output:
$temp -replace '(?s)^.*chars', ''
What am I doing wrong?
Edit.:
I need to use variable in regex, because I iterate through multiple strings like this one and not always the part I want to remove has the same string (example: some\string/with/**/special\chars\haha -> \haha; C:\st/h/*/1234\asdf\x -> \x). So in conclusion I have a problem using variable in regex, not with the regex itself as that works as intended when I replace variable with string (as shown above)
Try
$temp = "some\string/with/**/special\chars\haha"
$tmp="chars"
$regex = '(?s)^.*' + $tmp
$temp -replace $regex, ''
Looks like it's because you are using single quotes in your regex instead of double quotes. This means that the variable $tpm isn't being used.
Here is what your code should look like:
$temp = "some\string/with/**/special\chars\haha"
$tmp="chars"
$temp -replace "(?s)^.*$tmp", ''
Your code was using $tmp instead of the actual value inside the $tmp variable.

Split a string to multiple strings in powershell

I have strings like
$value = "1;#Mohapatra,Mrutyunjaya (ADM) 10;#Sumit Upadhyay(ADM) 11;#Naidu,Ishan(ADM)"
I want to retrieve
"Mohapatra,Mrutyunjaya (ADM)", "Sumit Upadhyay(ADM)", "Naidu,Ishan(ADM)"
from $value.
I have tried $value.Split(";#")[0]. It is returning the first parameter only. But I want all the parameters
Split your string at sequences of \s*\d+;# (optional whitespace, followed by a number, a semicolon, and a hash character), and remove empty elements from the resulting list:
$value -split '\s*\d+;#' | Where-Object { $_ }
Just FYI, if you want to declare each as a variable you can say $a,$b,$c,$d = $Value -Split (";#") and each of $a, $b, $c and $d will retain those values.