Powershell: Writing string with escaped characters to file - powershell

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

Related

How to fix Folder Path error in PowerShell

I have this folder in my OneDrive and Just wondering how should I pass the path. Right now I'm getting "Folder not Found" when I try to do it like this.
$ServerRelativeUrl= "Documents/PC-OFFBOARD_USMPGWNCAT15C61-GZJY8T-01-Dec-0257/C$/'$Windows.~WS'"
With double-quoted " strings, you must escape characters with special meanings if you want them to be processed literally. PowerShell's escape character is the backtick `. The dollar-symbol $ must be prefixed with a backtick like this to be part of a literal file path:
"Documents/PC-OFFBOARD_USMPGWNCAT15C61-GZJY8T-01-Dec-0257/C`$/'`$Windows.~WS'"
Alternatively, you can use a single-quoted ' string instead, making sure to escape the literal single-quotes with two single-quotes '' (backticks won't escape in a literal string):
'Documents/PC-OFFBOARD_USMPGWNCAT15C61-GZJY8T-01-Dec-0257/C$/''$Windows.~WS'''
This loses your ability to insert actual intended variables though. You can however rely on the format operator in this case. To insert the literal string '$Windows.~WS' into the path, for example:
$folderName = '''$Windows.~WS'''
$fullPath = 'Documents/PC-OFFBOARD_USMPGWNCAT15C61-GZJY8T-01-Dec-0257/C$/{0}' -f $folderName

Unrecognized escape sequence \P while parsing the data using 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.

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"

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.

Powershell escape character ` (backtick) not escaping string

I'm having trouble adding a new line into a string in powershell:
Get-ChildItem "C:\Users\matt\Desktop\CShell Install" |foreach {'<Component Id="'+$_.name+'" Guid="' +[guid]::NewGuid() + '">`r`n<File Id="'+$_.name+'" Source="$(var.CShell.TargetPath)"></File></Component>'}
As you can see I want a newline to occur at
``r`n
instead they are printed literally.
Any pointers?
Do not use Single Quote, where you want PowerShell to honor backticks (or any other characters that you need PowerShell to interpret).
"FirstLine`r`nSecondLine" prints
FirstLine
SecondLine
'"FirstLine`r`nSecondLine"' prints
"FirstLine`r`nSecondLine"