PowerShell Dobule Escape string with text "$__VAR__" - powershell

One of my scripts can be stripped down to the following code.
function Replace
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory, Position=0)]
[string]
$LiteralPath,
[Parameter(Mandatory, Position=1)]
[string]
$Expression,
[Parameter(Mandatory, Position=2)]
[string]
$Replacement
)
Get-Content $LiteralPath | ForEach-Object {$_ -replace $Expression, $Replacement } | Set-Content $LiteralPath + ".temp"
}
An example call to the script would be
Replace ".\MyFile.txt" "^#define abc.*" "#define abc 1"
I have run into a situation where the string I need to find and replace contains both dollar signs and underscores. The dollars signs must be escaped to prevent PowerShell from expanding the variable. One string contains a dollar sign followed by an underscore. This is causing an issue because PowerShell is not expanding the variable name but then is expanding the $_ piping variable. How can I prevent PowerShell from expanding both variable name and piping token.
This is an example cal to the function with a string I need to escape.
Replace ".\MyFile.txt" "^\#\`$__LIBRARY_DIR\\prj.gpj" "`$__LIBRARY_DIR\prj.gpj"
In this example the line of text which reads #$__LIBRARY_DIR\prj.gpj is getting changed to #$__LIBRARY_DIR\prj.gpj_LIBRARY_DIR\prj.gpj. I am looking for the text to be changed to $__LIBRARY_DIR\prj.gpj
Notice the $_ is expanded which I do not want it to expand. I have tried adding more escape characters but that only causes them to appear in the file. How can the string be escaped to prevent $_ from expanding?

In powershell, if you don't want variables to expand in your string, use 'single quotes' instead if "double quotes", that saves you the trouble of escaping the $ sign with backticks.
Now in your case you have the additional challenge, that the -replace operator will also want to expand expressions that start with the $ sign in the replacement string, regardless of the types of quotes that you use.
To tell -replace that you really want to see that $ in your replacement string, you need to write $$:
'#$__LIBRARY_DIR\prj.gpj' -replace '^#\$__LIBRARY_DIR\\prj.gpj','$$__LIBRARY_DIR\prj.gpj'
Note: As others have correctly pointed out in the comments, if your task is to strip expressions from a leading #, you can do that in a more simple way:
'#$__LIBRARY_DIR\prj.gpj' -replace '^#'
Or alternatively with the good old "trim":
'#$__LIBRARY_DIR\prj.gpj'.TrimStart('#')

Related

When using -replace in powershell to replace "../" with "ref/" the dots ("..") become a wildcard [duplicate]

So I am using a variable that contains a password and just recently found out that some of the passwords are containing special characters. I have no control over what the password is so I have to deal with whatever I am getting. I know the back tick '`' character is what is used to escape characters. The whole reason for this post is that I am finding passwords is text files and replacing the found password with a pattern of 'xxxxxxxxx'.
Currently the code I am using is this:
$pass = "DR$123asd##!"
Because the $pass variable contains the '$' character the $123asd is seen as a variable that has no value
$pass
so all you get is:
DR##!
If I change the pass variable like this
$pass = 'DR$123asd##!'
$pass
DR$123asd##!
Then the '$' character is ignored and the string is complete, but If I run the code:
$output | foreach-object { $_ -replace "$pass", 'xxxxxxxx' }
This is my password DR$123asd##!, It is a great password!
The password doesn't get replaced and I'm, not sure why.
-replace is a regular expression operator, and the $ sigil is indeed a special character in regex.
You can escape all regex meta-characters in a literal string with [regex]::Escape():
$output | foreach-object { $_ -replace [regex]::Escape("$pass"), 'xxxxxxxx' }

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

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.

Fix ANSI control characters before PowerShell output to a file

Is there anyway for PowerShell to output a file without ANSI control characters like color control, e.x. [1;xxm or [xm], before outputting to a file,
[1;35mStarting selenium server... [0m[1;35mstarted - PID: [0m 22860
[0;36m[Signin Test] Test Suite[0m
[0;35m================================[0m
Running: [0;32mstep 1 - launch the browser[0m
[1;35m[40mINFO[0m [1;36mRequest: POST /wd/hub/session[0m
The output displays correctly with color in PowerShell terminal, (I've used chcp, not working)
You could try something like this:
... | ForEach-Object {
$_ -replace '\[\d+(;\d+)?m' | Add-Content 'C:\path\to\output.txt'
$_
}
or wrap it in a function:
function Tee-ObjectNoColor {
[CmdletBinding()]
Param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
[string]$InputObject,
[Parameter(Position=1, Mandatory=$true)]
[string]$FilePath
)
Process {
$InputObject -replace '\[\d+(;\d+)?m' | Add-Content $FilePath
$InputObject
}
}
... | Tee-ObjectNoColor -FilePath 'C:\path\to\output.txt'
For the windows system one could use the Replace command available as a part of Powershell 3.0.The powershell makes use of regex expression that helps to replace the ANSI Color codes. (In case of UNIX one could use the sed command )
Using Regex
Below is the standard Regex for removing ANSI color codes (can be used in Linux and windows both)
'\x1b\[[0-9;]*m'
\x1b (or \x1B) is the escape special character
(sed does not support alternatives \e and \033)
\[ is the second character of the escape sequence
[0-9;]* is the color value(s) regex
m is the last character of the escape sequence
Final Command
I am here outputting the logs of docker to a log file.One could do the same for other commands
docker logs container | ForEach-Object { $_ -replace '\x1b\[[0-9;]*m','' }| Out-File -FilePath .\docker-logs.log
ForEach-Object refers to each object from the pipped stream and $_ refers to current object.
The above command will remove the special characters like [1;35m , [0m[1;3 and ^[[37mABC from the output stream.

(PowerShell) split string with escaped separator characters

The split module is often used to split Active Directory Distinguished Names and Canonical Names to RDNs conveniently forgetting about the escaped separator characters that might be used in OUs and CNs as:
Distinguished Name Example with an escaped comma:
CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com
Canonical Name Example with an escaped slash:
Domain.com/Test/Slash\/Test/Test User
There are several splitting examples on the internet that do not even mention this trap which might work for a long time but sooner or later will cause a lot of pain troubleshooting this programming flaw .
I don’t think there is an easy way to correctly split escaped strings using a Regular Expression (see also: Is there a pure regex split of a string containing escape sequences?).
.
Using negative lookbehind:
$text = 'CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com'
$text -split '(?<!\\),'
CN=Test User
OU=Comma\,Test
OU=Test
DC=domain
DC=com
$text = 'Domain.com/Test/Slash\/Test/Test User'
$text -split '(?<!\\)/'
Domain.com
Test
Slash\/Test
Test User
To summarize and complement the existing, helpful answers:
mjolinor's answer works well if you needn't worry about \\ appearing in the input as an escaped \.
If \\ were present, the solution would misinterpret the , in \\, as escaped (rather than an escaped \ followed by an unescaped ,).
iRon's own answer addresses that problem with a more sophisticated regex.
Additionally, you may want to remove the escape characters after splitting; building on a regex provided by Wiktor Stribiżew here and adding a -replace operation with regex \\(.):
PS> 'foo,bar\,baz,bang\\,last' -split '(?<=(?<!\\)(?:\\\\)*),' -replace '\\(.)', '$1'
foo
bar,baz
bang\
last
Here's a simple utility function that wraps the above, with a configurable separator and escape char.:
function Split-Text {
param(
[Parameter(Mandatory=$True)] [string] $Text,
[Parameter(Mandatory=$True)] [string] $Separator,
[string] $EscapeChar = '\'
)
$Text -split
('(?<=(?<!{0})(?:{0}{0})*){1}' -f [regex]::Escape($EscapeChar), [regex]::Escape($Separator)) `
-replace ('{0}(.)' -f [regex]::Escape($EscapeChar)), '$1'
}
# Sample call - yields the same as above.
Split-Text 'foo,bar\,baz,bang\\,last' ','
# With "/" as the separator - analogous output.
Split-Text 'foo/bar\/baz/bang\\/last' '/'
I think there is still a little trap as RNDs could potentially end with a backslash (which will be escaped with an additional backslash):
$text = 'CN=Test User,OU=EndSlash\\,OU=Comma\,Test,DC=domain,DC=com'
$text -split '(?<!\\),'
CN=Test User
OU=EndSlash\\,OU=Comma\,Test
DC=domain
DC=com
In other words the concerned separator should only be skipped if there is an odd number of backslashes in front of it.
To cover this, I think the complete regular expressions should be:
(?<![^\\](\\\\)*\\), (for Distinguished Names) and
(?<![^\\](\\\\)*\\)/ (for Canonical Names).
$text = 'CN=Test User,OU=EndSlash\\,OU=Comma\,Test,DC=domain,DC=com'
$text -split '(?<![^\\](\\\\)*\\),'
CN=Test User
OU=EndSlash\\
OU=Comma\,Test
DC=domain
DC=com
$text = 'Domain.com/Slash\/Test/EndSlash\\/Test/Test User'
$text -split '(?<![^\\](\\\\)*\\)/'
Domain.com
Slash\/Test
EndSlash\\
Test
Test User
Therefore I have created a little cmdlet that adds an escape feature to the existing split module:
Function Split {
param(
[Parameter(Mandatory = $True, ValueFromPipeline = $true)][String]$String,
[Parameter(Mandatory = $False, Position = 0)][String]$Delimiter = " ",
[Parameter(Mandatory = $False, Position = 1)][Int]$MaxSubstrings = 0,
[Parameter(Mandatory = $False, Position = 2)][String]$Escape,
[Parameter(Mandatory = $False, Position = 3)][String]$Options = ""
)
If ($Escape) {$String = $String.Replace("$Escape$Delimiter", [String][Char]27)}
$Split = $String -Split $Delimiter, $MaxSubstrings, $Options
If ($Escape) {$Split | ForEach {$_.Replace([String][Char]27, "$Escape$Delimiter")}} Else {$Split}
}
"CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com" | Split "," -Escape "\"
"Domain.com/Test/Slash\/Test/Test User" | Split "/" -Escape "\"