Powershell handle variable with special character (open bracket) - powershell

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!"
}

Related

To check for special characters in a string

I need to find special character in a string which has alphanumeric values in it.
I have tried the below code snippet but it doesn't work.
$Special_characters = ('\n|\r|\t|\a|\"|\`')
$Value = "g63evsy3swisnwhd83bs3hs9sn329hs\t"
if($Value -match $Special_characters)
{
Write-Host "Special characters are present"
}
else
{
Write-Host "special characters are absent"
}
The output says "special characters are absent" even though there are special characters at the end. How to resolve it?
$Special_Characters here is a string, so your code is searching for the whole word (\n|\r|\t|\a|\"|`) to be found in $Value, which is not found.
Instead of string, you have to use array as follows:
$Value = "g63evsy3swisnwhd83bs3hs9sn329hs\t"
$Special_Characters = #('\\n','\\r','\\t','\\a','\\"','\\`')
$Special_Characters | Foreach-Object {
if ($Value -match $_) {
"$_ is present"
} else {
"$_ is not present"
}
}
Note
You have to put double back-slash (\\) because backslash is considered as escape character in Powershell; Look here for further information about backslash in Powershell
There is a misunderstanding here.
The backslash is used to define a special character in a Regular Expression, as e.g. \t define a tab.
But this is not the case for PowerShell. To define a special character in PowerShell you need to use the backtick character (See: About Special Characters), e.g. a Tab is written as `t.
In other words, the regular expression pattern in the question is correct but the input string is not (in contrast to what the question/title suggests, there is in fact no special character in the given input string").
it should be:
"...hs9sn329hs`t" -match '\n|\r|\t|\a|\"|\`'
True
As it concerns a list of single (special) characters, you might also consider a bracket expression (rather than an OR "pipe" character) for this:
"...hs9sn329hs`t" -match '[\n\r\t\a\"\`]'
True
Visa versa: it is allowed to use special characters in a regular expression pattern using double quotes so that PowerShell will evaluate the string (but I recommend against this):
"...hs9sn329hs`t" -match "`n|`r|`t|`a|`"|``"
True
If the input string in the question is really the string you want to check upon (implying that you refer to the backslash as a special character, which formally is not), you want to check for a \t rather than a tab,. For this you will need to escape the backslashes in your regular expression to literally match the \t:
"...hs9sn329hs\t" -match '\\n|\\r|\\t|\\a|\\"|\\`'
True
Its an one-liner:
$Special_characters = '\n|\r|\t|\a|\"|\`'
$Value = "g63evsy3swisnwhd83bs3hs9sn329hs\t"
$result = #($Special_characters -split '\|' | % { $Value.Contains( $_ ) }).Contains( $true )
$result is true when a special character is found, otherwise false.
Here's all the special characters you referred to. You can try out a string by itself just to see if it works. It must be double quoted.
PS /Users/js> "`n`r`t`a`"``"
"`
You can also try out the -match operator by itself.
PS /Users/js> "`n`r`t`a`"``" -match '\n|\r|\t|\a|\"|\`'
True
About special characters: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-6

Split & Trim in a single step

In PS 5.0 I can split and trim a string in a single line, like this
$string = 'One, Two, Three'
$array = ($string.Split(',')).Trim()
But that fails in PS 2.0. I can of course do a foreach to trim each item, or replace ', ' with ',' before doing the split, but I wonder if there is a more elegant approach that works in all versions of PowerShell?
Failing that, the replace seems like the best approach to address all versions with a single code base.
TheMadTechnician has provided the crucial pointer in a comment on the question:
Use the -split operator, which works the same in PSv2: It expects a regular expression (regex) as the separator, allowing for more sophisticated tokenizing than the [string] type's .Split() method, which operates on literals:
PS> 'One, Two, Three' -split ',\s*' | ForEach-Object { "[$_]" }
[One]
[Two]
[Three]
Regex ,\s* splits the input string by a comma followed by zero or more (*) whitespace characters (\s).
In fact, choosing -split over .Split() is advisable in general, even in later PowerShell versions.
However, to be fully equivalent to the .Trim()-based solution in the question, trimming of leading and trailing whitespace is needed too:
PS> ' One, Two,Three ' -split ',' -replace '^\s+|\s+$' | ForEach-Object { "[$_]" }
[One]
[Two]
[Three]
-replace '^\s+|\s+$' removes the leading and trailing whitespace from each token resulting from the split: | specifies an alternation so that the subexpressions on either side of it are considered a match; ^\s+, matches leading whitespace, \s+$ matches trailing whitespace; \s+ represents a non-empty (one or more, +) run of whitespace characters; for more information about the -replace operator, see this answer.
(In PSv3+, you could simplify to (' One, Two,Three ' -split ',').Trim() or use the solution from the question.
To also weed out empty / all-whitespace elements, append -ne '')
As for why ('One, Two, Three'.Split(',')).Trim() doesn't work in PSv2: The .Split() method returns an array of tokens, and invoking the .Trim() method on that array - as opposed to its elements - isn't supported in PSv2.
In PSv3+, the .Trim() method call is implicitly "forwarded" to the elements of the resulting array, resulting in the desired trimming of the individual tokens - this feature is called member-access enumeration.
I don't have PS 2.0 but you might try something like
$string = 'One, Two, Three'
$array = ($string.Split(',') | % { $_.Trim() })
and see if that suits. This is probably less help for you but for future readers who have moved to future versions you can use the #Requires statement. See help about_Requires to determine if your platforms supports this feature.

Ignore Tokens on the Front of a Regular Expression in 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.

Find and replace a string containing both double quotes and brackets

Let's say I have a test file named testfile.txt containing the below line:
one (two) "three"
I want to use PowerShell to say that if the entire string exists, place a line directly underneath it with the value:
four (five) "six"
(Notice that it includes both spaces, brackets and double quotes. This is important, as the problem I am having is I think with escaping the brackets and double quotes).
So the result would be:
one (two) "three"
four (five) "six"
I thought the easiest way of doing it would be to say that if the first string is found, replace it with the first string itself again, and the new string forming a new line included in the same command. I had difficulty putting the strings in line so I tried using a herestring variable whereby an entire text block with formatting is read. It still does not parse the full string with quotes into the pipeline. I'm new to powershell so don't hold back if you see something stupid.
$herestring1 = #"
one (two) "three"
"#
$herestring2 = #"
one (two) "three"
four (five) "six"
"#
if((Get-Content testfile.txt) | select-string $herestring1) {
"Match found - replacing string"
(Get-Content testfile.txt) | ForEach-Object { $_ -replace $herestring1,$herestring2 } | Set-Content ./testfile.txt
"Replaced string successfully"
}
else {
"No match found"}
The above just gives "No match found" every time. This is because it does not find the first string in the file.
I have tried variations using backtick [ ` ] and doubling quotes to try to escape, but I thought the point in a here string was that it should parse the text block including all formatting so I should not have to.
If I change the file to contain only:
one two three
and then change the herestring accordingly to:
$herestring1 = #"
one two three
"#
$herestring2 = #"
one two three
four five six
"#
Then it works ok and I get the string replaced as I want.
As Martin points out, you can use -SimpleMatch with Select-String to avoid parsing it as a regular expression.
But -replace will still be using a regex.
You can escape the pattern for RegEx using [RegEx]::Escape():
$herestring1 = #"
one (two) "three"
"#
$herestring2 = #"
one (two) "three"
four (five) "six"
"#
$pattern1 = [RegEx]::Escape($herestring1)
if((Get-Content testfile.txt) | select-string $pattern1) {
"Match found - replacing string"
(Get-Content testfile.txt) | ForEach-Object { $_ -replace $pattern1,$herestring2 } | Set-Content ./testfile.txt
"Replaced string successfully"
}
else {
"No match found"}
Regular expressions interpret parentheses () (what you are calling brackets) as special. By default, spaces are not special, but they can be with certain regex options. Double quotes are no problem.
In regex, the escape character is backslash \, and this is independent of any escaping you do for the PowerShell parser using backtick `.
[RegEx]::Escape() will ensure anything special to regex is escaped so that a regex pattern will interpret it as literal, so your pattern will end up looking like this: one\ \(two\)\ "three"
Just use the Select-String cmdlet with the -SimpleMatch switch:
# ....
if((Get-Content testfile.txt) | select-string -SimpleMatch $herestring1) {
# ....
-SimpleMatch
Indicates that the cmdlet uses a simple match rather than a regular
expression match. In a simple match, Select-String searches the input
for the text in the Pattern parameter. It does not interpret the value
of the Pattern parameter as a regular expression statement.
Source.

How can I replace every comma with a space in a text file before a pattern using PowerShell

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 }