Case-insensitive PowerShell replacement - powershell

I have the following PowerShell script:
$RegExplorer = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
$NullSessionPipes = "$($RegExplorer.NullSessionPipes)"
$NullSessionPipes
$NullSessionPipes = $NullSessionPipes.replace("browser", "")
$NullSessionPipes
The script works fine as long as the registry key examining exactly matches the case I've specified - "browser".
However if the case was different in the registry key say "BROWSER" or "Browser" it doesn't do the replacement.
I'm looking for some way to make string.replace case insensitive. I know I could convert the string using .tolower or .toupper first to make comparison easier, but I don't know if this particular registry key or applications which access it are case sensitive, so I don't want to change the case of existing key.
Is there an easy way to do this?

Call me pedantic but while nobody here was outright wrong, nobody provided the correct code for the final solution either.
You need to change this line:
$NullSessionPipes = $NullSessionPipes.replace("browser", "")
to this:
$NullSessionPipes = $NullSessionPipes -ireplace [regex]::Escape("browser"), ""
The strange [regex] text isn't strictly necessary as long as there are no regular expression characters (ex. *+[](), etc) in your string. But you're safer with it. This syntax works with variables too:
$NullSessionPipes = $NullSessionPipes -ireplace [regex]::Escape($stringToReplace), $stringToReplaceItWith

NullSessionPipes is a multi-string value and the replace method (in addition of being case-sensitive) may fail if there's more than one string in it. You can use the -replace operator. By default, all comparison operators are case-insensitive. Case-sensitive operators starts with 'c', like: -creplace,-ceq, etc.
Operators that starts with 'i' are case-insensitive, like -ireplace,-ieq, and they are the same as -replace, -ieq.
See the about_Comparison_Operators for more information.

Use a regular expression replacement instead:
$RegExplorer = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
$NullSessionPipes = "$($RegExplorer.NullSessionPipes)"
$NullSessionPipes
$NullSessionPipes = $NullSessionPipes -replace "browser", ""
$NullSessionPipes

The .Replace method doesn't have a case-insensitive option:
String.Replace method
...This method performs an ordinal (case-sensitive and culture-insensitive) search...
https://msdn.microsoft.com/en-us/library/fk49wtc1(v=vs.110).aspx
The other answers to this question suggest using -replace or -ireplace, which is fine if you want to use regex replacement. However, as #Bob mentions in his (her?) comments this isn't always appropriate. An example might be if you want to include a literal string such as $_ in the replacement text.
One trick borrowed from other case-sensitive places is to convert the "input" string and the "search" string to lower case:
[PS]> "TeXT".ToLower().Replace("text","NewString")
NewString
However... this causes the output to be in lower case for anything that doesn't match the search string, which may well be unacceptable.
[PS]> "DON'T CHANGE MY TeXT".ToLower().Replace("text","NewString")
don't change my NewString

The String.Replace method has two different overloads that allows a case-insensitive replacement:
Replace(String, String, StringComparison)
Replace(String, String, Boolean, CultureInfo)
For the first one, you need to pick a StringComparison that ignores case, e.g. OrdinalIgnoreCase.
For the second one, it's the Boolean that controls case sensitivity; a null for the CultureInfo uses the current culture.
Using these in PowerShell 7 would look like:
$s = 'All the HIP cats'
$s.Replace('hip','cool') # doesn't work: All the HIP cats
$s.Replace('hip','cool','OrdinalIgnoreCase') # works : All the cool cats
$s.Replace('hip','cool',$true,$null) # works : All the cool cats

Related

Powershell - Need to recognize if there is more than one result (regex)

I am using this to find if file name contains exactly 7 digits
if ($file.Name -match '\D(\d{7})(?:\D|$)') {
$result = $matches[1]
}
The problem is when there is a file name that contains 2 groups of 7 digits
for an example:
patch-8.6.22 (1329214-1396826-Increase timeout.zip
In this case the result will be the first one (1329214).
For most cases there is only one number so the regex is working but I must to recognize if there is more than 1 group and integrated into the if ()
The -match operator only ever looks for one match.
To get multiple ones, you must currently use the underlying .NET APIs directly, specifically [regex]::Matches():
Note: There's a green-lighted proposal to implement a -matchall operator, but as of PowerShell 7.3.0 it hasn't been implemented yet - see GitHub issue #7867.
# Sample input.
$file = [pscustomobject] #{ Name = 'patch-8.6.22 (1329214-1396826-Increase timeout.zip' }
# Note:
# * If *nothing* matches, $result will contain $null
# * If *one* substring matches, return will be a single string.
# * If *two or more* substrings match, return will be an *array* of strings.
$result = ([regex]::Matches($file.Name, '(?<=\D)\d{7}(?=\D|$)')).Value
.Value uses member-access enumeration to extract matching substrings, if any, from the elements of the collection returned by [regex]::Matches().
I've tweaked the regex to use lookaround assertions ((?<=/...) and (?=...)) so that only the substrings of interest are captured.
See this regex101.com page for an explanation of the regex and the ability to experiment with it.

Returning the whole string when no match in a Powershell Substring(0, IndexOf)

I have some Powershell that works with mail from Outlook folders. There is a footer on most emails starting with text "------". I want to dump all text after this string.
I have added an expression to Select-Object as follows:
$cleanser = {($_.Body).Substring(0, ($_.Body).IndexOf("------"))}
$someObj | Select-Object -Property #{ Name = 'Body'; Expression = $cleanser}
This works when the IndexOf() returns a match... but when there is no match my Select-Object outputs null.
How can I update my expression to return the original string when IndexOf returns null?
PetSerAl, as countless times before, has provided the crucial pointer in a comment on the question:
Use PowerShell's -replace operator, which implements regex-based string replacement that returns the input string as-is if the regex doesn't match:
# The script block to use in a calculated property with Select-Object later.
$cleanser = { $_.Body -replace '(?s)------.*' }
If you want to ensure that ------ only matches at the start of a line, use (?sm)^------.*; if you also want to remove the preceding newline, use (?s)\r?\n------.*
(?s) is an inline regex option that makes . match newlines too, so that .* effectively matches all remaining text, across lines.
By not specifying a replacement operand, '' (the empty string) is implied, which effectively removes the matching part from the input string (technically, a copy of the original string with the matching part removed is returned).
If regex '(?s)------.*' does not match, $_.Body is returned as-is (technically, it is the input string itself that is returned, not a copy).
The net effect is that anything starting with ------ is removed, if present.
I agree with #mklement0 and #PetSerAl Regular Expressions give the best answer. Yay! Regular Expressions to the rescue!
Edit:
Fixing my original post.
Going with #Adam's ideas of using a script block in the expression, you simply need to add more logic to the script block to check the index first before using it:
$cleanser = {
$index = ($_.Body).IndexOf("------");
if($index -eq -1){
$index = $_.Body.Length
};
($_.Body).Substring(0, $index)
}
$someObj | Select-Object -Property #{ Name = 'Body'; Expression = $cleanser}

Zero being dropped from array

I have noticed is that if the 'version' ends in a zero, then that zero is being dropped.
So, for example, if the 'version' in the $statusContent is 1.9.7.680, then $version is 1.9.7.68. Is there a way to keep that zero?
$version = $statusContent.Content.Substring(145).TrimEnd('" counter="0" /></mibscalar>')
Sample string:
<mibscalar name="appRunningApp" type="readonly" link= "xxx.xxx.xxx.xxx/v1/mib/objs/appRunningApp?type=xml"; ><data index="1" value="ma xtime - 1.9.7.680" counter="0" /></mibscalar>
TrimEnd() is not the function you're looking for - it turns the string argument into a character array and eats away every occurrence of any of those characters from the end of the string until it can't find anymore.
Use Remove() instead to cut off the trailing part:
$string = $statusContent.Content.Substring(145)
$tail = '" counter="0" /></mibscalar>'
if($string.EndsWith($tail)){
# [string]::Remove() takes a start index as it's first argument
# Let's calculate the index at which we'll start removing characters
$string = $string.Remove($string.Length - $tail.Length)
}
Why not use regular expression?
> $txt
<mibscalar name="appRunningApp" type="readonly" link= "127.0.0.2/v1/mib/objs/appRunningApp?type=xml"; ><data index="1" value="ma xtime - 1.9.7.680" counter="0" /></mibscalar>
> if ($txt -match '\d+[.]\d+[.]\d+[.]\d+(?=\")') { $version = $matches[0] }
> $version
1.9.7.680
EDIT:
Now that I'm aware xxx.xxx.xxx.xxx bit was an IP address I had to change regular expression in above code to avoid matching it. IP addresses are too similar in their pattern to versions to tell them apart looking just at them.
I've added a lookahead (?=\") that matches only if " follows the pattern. It will work as long as quotes closes immediately after version number. You could as another option have a negative lookahead (?!\/) to make sure / is not following a pattern to make sure it's not and <IP>/<path> kind of string.

String.Trim() not removing characters in a string

I need to create a String from double the use String.Trim() to remove the full stop, but it doesn't remove it. I think there is also a way to do this numerically but I'd like to do it with the string. Is there a reason it won't remove it? The output from the code is 5.5
$MyDouble = 5.5
[String]$MyDouble2 = $MyDouble
$MyDouble2.Trim(".")
$MyDouble2
String.Trim() only trims from the beginning and end of strings, so it has no effect in your command, because the . only occurs inside your input string.
If you truly want to remove just the . and keep the post-decimal-point digits, use the -replace operator:
$MyDouble2 -replace '\.' # -> '55'
Note:
* -replace takes a regex (regular expression) as the search operand, hence the need to escape regex metacharacter . as \.
* The above is short for $MyDouble2 -replace '\.', ''. Since the replacement string is the empty string in this case, it can be omitted.
If you only want to extract the integer portion, use either 4c74356b41's .Split()-based answer, or adapt the regex passed to -replace to match everything from the . through the end of the string.
$MyDouble2 -replace '\..*' # -> '5'
#Matt mentions the following alternatives:
For removing the . only: Using String.Replace() to perform literal substring replacement (note how . therefore does not need \-escaping, as it did with -replace, and that specifying the replacement string is mandatory):
$MyDouble2.Replace('.', '') # -> '55'
For removing the fractional part of the number (extracting the integer part only), using a numerical operation directly on $MyDouble (as opposed to via the string representation stored in $MyDouble2), via Math.Floor():
[math]::Floor($MyDouble) # -> 5 (still a [double])
Looking at some documentation for .Trim([char[]]) you will see that
Removes all leading and trailing occurrences of a set of characters specified in an array from the current String object.
That does not cover the middle of strings, so using the .Replace() method would accomplish that.
I think there is also a way to do this numerically but I'd like to do it with the string.
Just wanted to mention that converting numbers to strings to then drop decimals via string manipulation is a poor approach. Assuming your example is what you are actually trying to do, I suggest using a static method from the [math] class instead.
$MyDouble = 5.5
[math]::Floor($MyDouble)
$MyDouble = 5.5
[String]$MyDouble2 = $MyDouble
$MyDouble2.Replace(".", "")
Well, why would it trim not the last (or first) character? It wouldn't, what you need (probably) is:
$MyDouble = 5.5
[String]$MyDouble2 = $MyDouble
$MyDouble2.Split(".")[0]
$MyDouble = 5.5
[String]$MyDouble2 = $MyDouble
$res=$MyDouble2 -split "\."
$res[0..($res.Count-1)] -join ""

Powershell - remove currency formatting from a number

can you please tell me how to remove currency formatting from a variable (which is probably treated as a string).
How do I strip out currency formatting from a variable and convert it to a true number?
Thank you.
example
PS C:\Users\abc> $a=($464.00)
PS C:\Users\abc> "{0:N2}" -f $a
<- returns blank
However
PS C:\Users\abc> $a=-464
PS C:\Users\abc> "{0:C2}" -f $a
($464.00) <- this works
PowerShell, the programming language, does not "know" what money or currency is - everything PowerShell sees is a variable name ($464) and a property reference (.00) that doesn't exist, so $a ends up with no value.
If you have a string in the form: $00.00, what you can do programmatically is:
# Here is my currency amount
$mySalary = '$500.45'
# Remove anything that's not either a dot (`.`), a digit, or parentheses:
$mySalary = $mySalary -replace '[^\d\.\(\)]'
# Check if input string has parentheses around it
if($mySalary -match '^\(.*\)$')
{
# remove the parentheses and add a `-` instead
$mySalary = '-' + $mySalary.Trim('()')
}
So far so good, now we have the string 500.45 (or -500.45 if input was ($500.45)).
Now, there's a couple of things you can do to convert a string to a numerical type.
You could explicitly convert it to a [double] with the Parse() method:
$mySalaryNumber = [double]::Parse($mySalary)
Or you could rely on PowerShell performing an implicit conversion to an appropriate numerical type with a unary +:
$mySalaryNumber = +$mySalary