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.
Related
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 #.
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|
I have a filepath, and I'm trying to remove the last two occurrences of the / character into . and also completely remove the '{}' via Powershell to then turn that into a variable.
So, turn this:
xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Into this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I've tried to get this working with the replace cmdlet, but this seems to focus more on replacing all occurrences or the first/last occurrence, which isn't my issue. Any guidance would be appreciated!
Edit:
So, I have an excel file and i'm creating a powershell script that uses a for each loop over every row, which amounts to thousands of entries. For each of those entries, I want to create a secondary variable that will take the full path, and save that path minus the last two slashes. Here's the portion of the script that i'm working on:
Foreach($script in $roboSource)
{
$logFileName = "$($script.a).txt".Replace('(?<=^[^\]+-[^\]+)-','.')
}
$script.a will output thousands of entries in this format:
xxx-xxx-xx\xxxxxxx\x{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Which is expected.
I want $logFileName to output this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I'm just starting to understand regex, and I believe the capture group between the parenthesis should be catching at least one of the '\', but testing attempts show no changes after adding the replace+regex.
Please let me know if I can provide more info.
Thanks!
You can do this in two fairly simply -replace operations:
Remove { and }
Replace the last two \:
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
$str -replace '[{}]' -replace '\\([^\\]*)\\([^\\]*)$','.$1.$2'
The second pattern matches:
\\ # 1 literal '\'
( # open first capture group
[^\\]* # 0 or more non-'\' characters
) # close first capture group
\\ # 1 literal '\'
( # open second capture group
[^\\]* # 0 or more non-'\' characters
) # close second capture group
$ # end of string
Which we replace with the first and second capture group values, but with . before, instead of \: .$1.$2
If you're using PowerShell Core version 6.1 or newer, you can also take advantage of right-to-left -split:
($str -replace '[{}]' -split '\\',-3) -join '.'
-split '\\',-3 has the same effect as -split '\\',3, but splitting from the right rather than the left.
A 2-step approach is simplest in this case:
# Input string.
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
# Get everything before the "{"
$prefix = $str -replace '\{.+'
# Get everything starting with the "{", remove "{ and "}",
# and replace "\" with "."
$suffix = $str.Substring($prefix.Length) -replace '[{}]' -replace '\\', '.'
# Output the combined result (or assign to $logFileName)
$prefix + $suffix
If you wanted to do it with a single -replace operation (with nesting), things get more complicated:
Note: This solution requires PowerShell Core (v6.1+)
$str -replace '(.+)\{(.+)\}(.+)',
{ $_.Groups[1].Value + $_.Groups[2].Value + ($_.Groups[3].Value -replace '\\', '.') }
Also see the elegant PS-Core-only -split based solution with a negative index (to split only a fixed number of tokens off the end) in Mathias R. Jessen's helpful answer.
try this
$str='xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
#remove bracket and split for get array
$Array=$str -replace '[{}]' -split '\\'
#take all element except 2 last elements, and concat after last elems
"{0}.{1}.{2}" -f ($Array[0..($Array.Length -3)] -join '\'), $Array[-2], $Array[-1]
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.
I have a text file with lines in this format:
FirstName,LastName,SSN,$x.xx,$x.xx,$x.xx
FirstName,MiddleInitial,LastName,SSN,$x.xx,$x.xx,$x.xx
The lines could be in either format. For example:
Joe,Smith,123-45-6789,$150.00,$150.00,$0.00
Jane,F,Doe,987-65-4321,$250.00,$500.00,$0.00
I want to basically turn everything before the SSN into a single field for the name thus:
Joe Smith,123-45-6789,$150.00,$150.00,$0.00
Jane F Doe,987-65-4321,$250.00,$500.00,$0.00
How can I do this using PowerShell? I think I need to use ForEach-Object and at some point replace "," with " ", but I don't know how to specify the pattern. I also don't know how to use a ForEach-Object with a $_.Where so that I can specify the "SkipUntil" mode.
Thanks very much!
Mathias is correct; you want to use the -replace operator, which uses regular expressions. I think this will do what you want:
$string -replace ',(?=.*,\d{3}-\d{2}-\d{4})',' '
The regular expression uses a lookahead (?=) to look for any commas that are followed by any number of any character (. is any character, * is any number of them including 0) that are then followed by a comma immediately followed by a SSN (\d{3}-\d{2}-\d{4}). The concept of "zero-width assertions", such as this lookahead, simply means that it is used to determine the match, but it not actually returned as part of the match.
That's how we're able to match only the commas in the names themselves, and then replace them with a space.
I know it's answered, and neatly so, but I tried to come up with an alternative to using a regex - count the number of commas in a line, then replace either the first one, or the first two, commas in the line.
But strings can't count how many times a character appears in them without using the regex engine(*), and replacements can't be done a specific number of times without using the regex engine(**), so it's not very neat:
$comma = [regex]","
Get-Content data.csv | ForEach {
$numOfCommasToReplace = $comma.Matches($_).Count - 4
$comma.Replace($_, ' ', $numOfCommasToReplace)
} | Out-File data2.csv
Avoiding the regex engine entirely, just for fun, gets me things like this:
Get-Content .\data.csv | ForEach {
$1,$2,$3,$4,$5,$6,$7 = $_ -split ','
if ($7) {"$1 $2 $3,$4,$5,$6,$7"} else {"$1 $2,$3,$4,$5,$6"}
} | Out-File data2.csv
(*) ($line -as [char[]] -eq ',').Count
(**) while ( #counting ) { # split/mangle/join }