Unrecognized escape sequence \P while parsing the data using powershell - powershell

I am using below properties and powershell script to parse the data from properties file. But it is failing while trying to read backslash character "\" in the datasource string. I tried to fix it by adding additional backslash in the properties file. But I am looking for the solution to handle it in powershell instead of making any changes in the properties file.
#env.properties value
datasource=SERVER.SUBDOMAIN.SUBDOMAIN2.DOMAIN.TLD\PWDFC1;initialcatalog=SAA;Trusted_Connection=True;MultiSubnetFailover=True
#Powershell Script
$EnvProps='C:\Apps\SAA\config\env.properties'
$AppConfig= 'C:\Apps\SAA\config\app.config'
$ReadProps = convertfrom-stringdata ([IO.File]::ReadAllText($EnvProps))
foreach($key in $ReadProps.Keys)
{
$value = $ReadProps[$key]
(Get-Content $AppConfig) -replace "##$key##", $value | Set-Content $AppConfig
}
Error while running above script
INFO 2021-08-30 14:22:37 Executing command ...
INFO 2021-08-30 14:22:37
ERROR 2021-08-30 14:22:53 convertfrom-stringdata : parsing "data
source=SERVER.SUBDOMAIN.SUBDOMAIN2.DOMAIN.TLD\PWDFC1;initial
ERROR 2021-08-30 14:22:53 catalog=SAA;Trusted_Connection=True;MultiSubnetFailover=True" -
Unrecognized escape sequence \P.

You need to escape backslashes in the data string. Per the documentation:
ConvertFrom-StringData supports escape character sequences that are allowed by conventional machine translation tools.... the cmdlet can interpret backslashes (\) as escape characters.... instead of the PowerShell backtick character (`) that would normally signal the end of a line in a script. Inside the here-string, the backtick character does not work.
Further mentioned in the link above, it supports escape sequences which are used by regular expressions. You can escape your string prior to putting it through ConvertFrom-StringData by using the [regex]::Escape(string) static method. This will automatically escape any expression tokens in your data so they are parsed literally.
You also need separate each key and value so they fit on their own line, though this is something you will run into once you resolve the error above. Alternatively, you can split on the semi-colon ; prior to running the string through ConvertFrom-StringData. This is also mentioned in the cmdlet documentation.
In the end, the data string you pass to ConvertFrom-StringData should look like this:
datasource=SERVER.SUBDOMAIN.SUBDOMAIN2.DOMAIN.TLD\\PWDFC1
initialcatalog=SAA
Trusted_Connection=True
MultiSubnetFailover=True
Edit
I missed this on the first go, but you also need to remove the semi-colons ; from the string before using ConvertFrom-StringData. This won't cause any errors, but the ; will be included as part of the value, which is likely undesirable. I have updated the sample data above to reflect this.

Related

Powershell: Writing string with escaped characters to file

I have a project where I'm trying to dynamically prepare a Powershell script, then write it out to a file for later execution.
Here's a minimal example of the issue:
# The string that I want to write out, having most special characters
$_output_str = "~##$%&*()-_=+{}[]<>?/.,'`"``;!"
# Write it out directly
Write-Output "$_output_str"
This is putting a string with escaped special characters into a variable, then writing the value of that variable out to stdout. It works as expected:
~##$%&*()-_=+{}[]<>?/.,'"`;!
Now, let's try storing that command in a file for later execution.
# The string that I want to write out, having most special characters
$_output_str = "~##$%&*()-_=+{}[]<>?/.,'`"``;!"
# Turn it into a string command
$_cmd = "Write-Output `"$_output_str`""
# Write the command out to a file
"$_cmd" | Out-File "execution.ps1"
And now let's try executing that file:
powershell.exe .\execution.ps1
This throws an error:
At C:\{MYPATH}\execution.ps1:1 char:43
+ Write-Output "~##$%&*()-_=+{}[]<>?/.,'"`;!"
+ ~
The string is missing the terminator: ".
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
OK, so let's look at the content that was written to the file. This is what execution.ps1 contains:
Write-Output "~##$%&*()-_=+{}[]<>?/.,'"`;!"
Notice that the " and the backtick near the end of the string are no longer escaped. So for some reason, when creating the $_cmd string, it got rid of my escape sequences.
How can I prevent this from happening?
Use single-quoted here-string for the value so it doesn't get expanded.
$_output_str = #'
~##$%&*()-_=+{}[]<>?/.,'"`;!
'#
You can also use regular single-quoted strings but keep in mind that you'll have to double any single-quote part of the string.
# The single quote is escaped by doubling it, meaning the final
# representation will be slightly different
$_output_str = '~##$%&*()-_=+{}[]<>?/.,''"`;!'
# Actual value (the two '' were escaped into a single ')
# '~##$%&*()-_=+{}[]<>?/.,'"`;!'
It is still better than double-quoted strings in any cases.
When you use a double-quoted strings, everything that can get evaluated get evaluated. Your backticks serves to escape the character right after but they are not part of the final representation of the string.
Double-quoted strings
A string enclosed in double quotation marks is an expandable string.
Variable names preceded by a dollar sign ($) are replaced with the
variable's value before the string is passed to the command for
processing.
...
expressions are evaluated, and the result is inserted in the string.
I recommend you take a look at the official documentation for more information. About_Quoting_Rules

How can I still get each line of a text file when the variable is in quotations?

I've been messing arround with Powershell and googling various things as I go along. This one is a little hard to put into words that google woule understand. I can get the indevidual lines of a text file in powershell by indexing:
$textFile = Get-Content "myText.txt"
$textFile[0]
This would output the first line of the text file. But when I put the text file in quotes it will output all lines, even with the index
"$textFile[0]"
How can I still get only get the line I want, while wrapping the variable in quotes? If I try "$textFile"[0] it will just give me the whole file as before. The reason I'm trying to do this is because I'm trying to make that one line of the text file part of a bigger string that I can execute
$remote = "Enter-PSSession -ComputerName`", textFile[0]"
Invoke-Expression $remote
This is my way of illustrating what I'm trying to do.
You can use any of the following methods:
# Sub-expression operator
"Some Text $($textFile[0])"
# String format operator
"My Text {0}" -f $textFile[0]
# Concatenation
("Text"+$textFile[0])
Surrounding double quotes tells PowerShell to expand the string inside. Any variables within will be interpolated. Variables begin with $ and their following names can only have certain characters without requiring a special escape. [ would require an escape and since it isn't escaped, PowerShell interprets the variable name ending with the character just before the [. Therefore $textFile is interpolated, the whole file contents are converted into a string, and [0] is appended to the end of the string.
You can see details of the operators at About_Operators.
See About_Variables for how to create a variable including cases with special characters even if that doesn't directly apply here.

How to get around using apostrophe in double quotes with Powershell

I am working with Powershell. My issue is that my file path (which does not exist on a local computer) has an apostrophe in it. Powershell is seeing this as a single quote, so it is giving me the following error: The string is missing the terminator: '. I thought that I could escape the single quote using a backtick, but that gave me the same error.
The error does not occur when I am doing the first line of code, and I don't even need the backtick for that part. I can even see that the contents of the variable matches up with the file path that I am using. It is only when I am doing the invoke-expression part that it is giving me the error.
I am using https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7, so I don't think the second line of the code is the problem.
My code is listed below:
$code = "\\example\example\John_Doe`'s_Folder\example.ps1"
invoke-expression -command $code
I have also tried wrapping the entire file path in double-quotes and single-quotes, but my program did not like that either. I can't remove the apostrophe as we have over a hundred of systems that are directing to John_Doe's_Folder.
Invoke-Expression should generally be avoided; definitely don't use it to invoke a script or external program.
In your case, simply use &, the call operator to invoke your script via the path stored in variable $code (see this answer for background information), in which case the embedded ' needs no escaping at all:
$code = "\\example\example\John_Doe's_Folder\example.ps1"
& $code
As for what you tried:
"\\example\example\John_Doe`'s_Folder\example.ps1" turns into the following verbatim string content:
\\example\example\John_Doe's_Folder\example.ps1
That is, the ` was removed by PowerShell's parsing of the "..." string literal itself, inside of which ` acts as the escape character; since escape sequence `' has no special meaning, the ` is simply removed.
For the ` to "survive", you need to escape the ` char. itself, which you can do with ``:
"\\example\example\John_Doe``'s_Folder\example.ps1"

Powershell output to clipboard without trailing newline?

I need to output the content of a powershell variable to the clipboard, preserving all the newline characters except for the last -trailing- one.
At the moment I am just piping the output of a variable readout to clip.exe, but that gives a trailing newline.
$Text = "line1`nline2"
$Text | clip.exe
gives the following:
"line1,
line2
"
I would like it to output
"line1,
line2"
How might I achieve this?
Using the pipeline can result in a new line being added by powershell. You can use Set-Clipboard and it should avoid the newline issue.
You can also use the .NET option as well:
[System.Windows.Forms.Clipboard]::SetText("line1`r`nline2")

Using PowerShell to replace string that contains double quotes in the string [duplicate]

I have a DOS batch file that has a line that executes a powershell script. First I tried a very simple script with this line in the batch file:
powershell -command "get-date" < nul
That worked great. But the script has nested double-quote characters, which can sometimes be escaped with a backtick (`) character. So then I tried this:
powershell -command "Write-Host `"hello world`"" < nul
That also worked great. However, the script I need to run is pretty complicated and has more than one level of nested double-quote characters. I have taken the complicated script and simplified it to an example that has the same principles here:
[string]$Source = " `"hello world`" ";
Write-Host $Source;
If I save this script inside a PS script file and run it, it works fine, printing out “hello world” including the double quotes, but I need to embed it in the line in the batch file. So I take the script and put it all on one line, and try to insert it into the batch file line, but it doesn’t work. I try to escape the double-quotes, but it still doesn’t work, like this:
powershell -command "[string]$Source = `" `"hello world`" `";Write-Host $Source;" < nul
Is there a way to do what I want? You might ask why I am doing this, but it’s a long story, so I won’t go into the details.
thanks
You'll have to use a combination of batch's escape character and PowerShell's escape character.
In batch, when escaping quotes, you use the common shell backslash (\) to escape those quotes. In Powershell, you use the backtick `.
So if you wanted to use batch to print out a quoted string with Powershell, you need to first batch escape the quote to declare the variable in Powershell, then to ensure the string is quoted you need batch and Powershell escape another quote, and then your add your desired string, ensuring you batch escape first.
For your example, this will work:
powershell -command "[string]$Source = \"`\"hello world`\"\"; Write-Host $Source;"
Here's a break down of the declaration of the $Source variable:
"[string]$Source = # open quote to begin -command parameter declaration
\" # batch escape to begin the string portion
`\" # Powershell+Batch escape
hello world # Your content
`\" # Posh+Batch again
\"; # Close out the batch and continue
more commands " # Close quote on -command parameter
This renders the string like this in batch:
`"hello world`"
One note, you don't need to explicitly cast $Source as a string since you are building it as a literal string from scratch.
$Source = "string stuff" will work as intended.