How to -replace continuous special characters? - powershell

In PowerShell when trying to replace
"Columnname1||colunnname2||kjhsadjhj|kjsad" -replace "[||]", "','"
above command is doing
"Columnname1','','colunnname2','','kjhsadjhj','kjsad"
but I'd like to replace the exact match like below
"Columnname1','colunnname2','kjhsadjhj|kjsad"

Your code doesn't do what you want because your search pattern defines a character class. Square brackets in a regular expression will match exactly one occurrence of any of the enclosed characters, even if you specify a character multiple times. [||] will thus match exatly one | character.
Since you apparently don't actually want to use a regular expression match I'd recommend doing a normal string replacement via the Replace() method rather than a regular expression replacement via the -replace operator:
"Columnname1||colunnname2||kjhsadjhj|kjsad".Replace('||', "','")
If you want to stick with a regular expression replacement you must specify two literal | characters, either by escaping them, as PetSerAl suggested
"Columnname1||colunnname2||kjhsadjhj|kjsad" -replace '\|\|', "','"
or by putting each of them in its own character class
"Columnname1||colunnname2||kjhsadjhj|kjsad" -replace '[|][|]', "','"

The regex pattern [||] means "1 of | or one of |"
Change it to \|{2} to match two consecutive pipes:
"Columnname1||colunnname2||kjhsadjhj|kjsad" -replace "\|{2}", "','"

Just in case your end result should be:
'Columnname1','colunnname2','kjhsadjhj|kjsad'
$string = '"Columnname1||colunnname2||kjhsadjhj|kjsad"'
$string
$String = $string -replace '^"|"$',"'" -replace '\|{2}',"','"
$string
Sample output:
"Columnname1||colunnname2||kjhsadjhj|kjsad"
'Columnname1','colunnname2','kjhsadjhj|kjsad'

Related

add quotation mark to a text file powershell

I need to add the quotation mark to a text file that contains 500 lines text.
The format is inconsistent. It has dashes, dots, numbers, and letters. For example
1527c705-839a-4832-9118-54d4Bd6a0c89
16575getfireshot.com.FireShotCaptureWebpageScreens
3EA2211E.GestetnerDriverUtility
I have tried to code this
$Flist = Get-Content "$home\$user\appfiles\out.txt"
$Flist | %{$_ -replace '^(.*?)', '"'}
I got the result which only added to the beginning of a line.
"Microsoft.WinJS.2.0
The expected result should be
"Microsoft.WinJS.2.0"
How to add quotation-mark to the end of each line as well?
There is no strict need to use a regex (regular expression) in your case (requires PSv4+):
(Get-Content $home\$user\appfiles\out.txt).ForEach({ '"{0}"' -f $_ })
Array method .ForEach() processes each input line via the script block ({ ... }) passed to it.
'"{0}"' -f $_ effectively encloses each input line ($_) in double quotes, via -f, the string-format operator.
If you did want to use a regex:
(Get-Content $home\$user\appfiles\out.txt) -replace '^|$', '"'
Regex ^|$ matches both the start (^) and the end ($) of the input string and replaces both with a " char., effectively enclosing the input string in double quotes.
As for what you tried:
^(.*?)
just matches the very start of the string (^), and nothing else, given that .*? - due to using the non-greedy duplication symbol ? - matches nothing else.
Therefore, replacing what matched with " only placed a " at the start of the input string, not also at the end.
You can use regex to match both:
The beginning of the line ^(.*?)
OR |
The End of the line $
I.e. ^(.*?)|$
$Flist = Get-Content "$home\$user\appfiles\out.txt"
$Flist | %{$_ -replace '^(.*?)|$', '"'}

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.

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 }

Replace character from link with powershell

I would like replace ? in
"EquipmentInfo["?"] = "<iframe src='http://bing.fr'></iframe>";"
by a variable.
I tried this:
(get-content C:\word.txt) -replace '?', '$obj' | Set-Content C:\word.txt
I would use a positive lookbehind to ensure you find the right question mark. Also you have to use double quote on your replacement since you want to replace a variable:
(get-content C:\word.txt -raw) -replace '(?<=EquipmentInfo\[")\?', "$obj" | Set-Content C:\word.txt
Regex used:
(?<=EquipmentInfo\[")\?
This answer explains the original problem.
jisaak's helpful answer provides a comprehensive solution.
The -replace operator takes a regular expression as the first operand on the RHS, in which ? is a so-called metacharacter with special meaning.
Thus, to use a literal ?, you must escape it, using \:
(get-content C:\word.txt) -replace '\?', $obj
Note: Do not use '...' around $obj, unless you want literal string $obj; generally, to reference variables inside strings you must use "...", but that's not necessary here.
A simple example with a literal:
'Right?' -replace '\?', '!' # -> 'Right!'

How do I match "|" in a regular expression in PowerShell?

I want to use a regular expression to filter out if a string contains one of "&" or "|" or "=". I tried:
$compareRegex = [String]::Join("|", #("&","|", "="));
"mydfa" -match $compareStr
PowerShell prints "True". This is not what I wanted, and it seems "|" itself has confused PowerShell for a matching. How do I fix it?
#Kayasax answer would do in this case (thus +1), just wanted to suggest more general solution.
First of all: you are not using the pattern that you've just created. I suspect $compareStr is $null, thus it will match anything.
To the point: if you want to create pattern that will match characters/strings and you can't predict if any of them will be/contain special character or not, just use [regex]::Escape() for any item you want to match against:
$patternList = "&","|", "=" | ForEach-Object { [regex]::Escape($_) }
$compareRegex = $patternList -join '|'
"mydfa" -match $compareRegex
In such a case input can be dynamic, and you won't end up with pattern that matches anything.
The | has a special meaning in regular expressions. Alternations (lists of alternative matches) are separated by this character. For instance, the expression
a|b|c
would match either a or b or c.
For matching a literal | you need to escape it with backslash (\|) or put it in a character class ([|]), so your expression should look like this:
"mydfa" -match "\||&|="
or like this:
"mydfa" -match "[|&=]"