I have a Powershell script that runs fine in VSCode but from the Powershell Prompt, I'm getting an error. Below is the output.
→ C:\WINDOWS\system32› powershell.exe -file 'D:\Source\Repos\Powershell Scripts\SD-Report-Archive.ps1' -sourcePath 'D:\Archives\' -targetPath 'D:\Archives2\'
D:\Archives\
Get-ChildItem : Cannot find path 'D:\A' because it does not exist.
At D:\Source\Repos\Powershell Scripts\SD-Report-Archive.ps1:25 char:14
+ $files = Get-ChildItem -Recurse -File -Path $sourcePath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (D:\A:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
As you can see on the 2nd line of the output I do a Write-Output of the parameter value that I am sending in and it's correct. When I execute Get-ChildItem it seems to truncate the value to 'D:\A' and I don't know why.
Param(
[Parameter(Mandatory = $true)]
[string]$sourcePath,
[Parameter(Mandatory = $true)]
[string]$targetPath
)
function Copy-FilesIntoFolders {
param()
Write-Output $sourcePath;
$files = Get-ChildItem -Path $sourcePath -Recurse -File
...
}
On Windows, PowerShell - of necessity - rebuilds the command line in order to invoke external programs.
Notably, most external programs don't understand single-quoted strings ('...') via their CLI, so after having performed its own parsing, PowerShell re-quotes the resulting (stringified) arguments using double quotes ("...") if it deems that necessary.
Unfortunately, this re-quoting is broken in several respects:
If the argument value doesn't contain spaces, no quoting is applied. Values without spaces but with special characters may therefore break commands, especially when another shell, such as cmd.exe is invoked.
E.g., cmd /c echo 'a&b' breaks, because a&b is ultimately passed without quotes, and & has special meaning in cmd.exe
If the argument has embedded double quotes (" chars.), the re-quoting does not automatically escape them for syntactically correct embedding inside "..." or unquoted literal use:
E.g., foo.exe 'Nat "King" Cole' is translated to foo.exe "Nat "King" Cole" - note the lack of escaping of the inner " chars. - which results in a different string when parsed by most applications, namely Nat King Cole (no double quotes).
You have to perform escaping manually, in addition to PowerShell's own escaping requirements, if applicable: foo.exe 'Nat \"King\" Cole' or, with double-quoting, foo.exe "Nat \`"King\`" Cole" (sic).
Similarly - as in your case - if the argument has spaces and ends in \, that trailing \ is not escaped in the resulting double-quoted string, which breaks the argument syntax:
E.g., foo.exe 'a b\' c becomes foo.exe "a b\" c - however, most programs - including PowerShell's own CLI - interpret the \" as an escaped " char. rather than the closing double quote, resulting in misinterpretation of the argument, merging it with the next argument to result in a b" c
Again you have to perform escaping manually, by doubling the \: foo.exe 'a b\\' c
Alternatively, if the argument happens to be a directory path whose trailing \ is optional, simply omit the latter.
Get-ChildItem : Cannot find path 'D:\A' because it does not exist.
The backslash (\) here looks like an escape character to me. Please try with \\
Related
I have a script that accepts two strings, each are delimited by '|-|'. The second string can contain any character other than '.
To call the script I do:
PowerShell .\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}'
It raises an error on the character }. How can I resolve this?
Edit:
It also has an issue with the - from the delimiters and raises the same error:
At line:1 char:54
+ .\Script.ps1 -arg2 <redacted>|-|<redacted> ...
+ ~
Missing expression after unary operator '-'.
At line:1 char:53
+ .\Script.ps1 -arg1 <redacted>|-|<redacted> ...
+ ~
Expressions are only allowed as the first element of a pipeline.
It does that at every instance of |-|. It does it with the brackets if I change the delimiter to /-/:
At line:1 char:168
+ ... <redacted>/-/<redacted>!OW}:Lj<redacted> ...
+ ~
Unexpected token '}' in expression or statement.
At line:1 char:181
+ ... /-/<redacted>cO0}e]k<redacted> ...
+ ~
Unexpected token '}' in expression or statement.
The beginning of the script is this:
Param
(
[switch] $enable,
[string] $arg1,
[string] $arg2
)
There's generally no need to call the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+) from inside PowerShell.
Assuming you're running Windows PowerShell (or, to generalize, the same edition of PowerShell), you can invoke your script directly:
.\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}'
If you still want to call the CLI - which is expensive, because it involves creating a child process, and also results in a loss of type fidelity - use a script block ({ ... }), but note that this works only from inside PowerShell (though it works across both PowerShell editions, i.e., you can call pwsh.exe from Windows PowerShell, and powershell.exe from PowerShell (Core) 7+)
powershell { .\script.ps1 -arg1 'Hello|-|World' -arg2 'random|-|dwua0928]43d}' }
If you were to call from outside PowerShell, it is best to use the -File CLI parameter rather than the (implied in Windows PowerShell) -Command parameter:
powershell -File .\script.ps1 -arg1 "Hello|-|World" -arg2 "random|-|dwua0928]43d}"
While this works from inside PowerShell too, the disadvantage compared to the { ... } approach is that the output is only ever text, whereas the { ... } approach is - within limits - capable of preserving the original data types - see this answer for more information.
In order to make a -Command-based command line work, enclose the PowerShell code in "..." as a whole, in which case the embedded '...' strings are passed as such:
powershell -Command ".\script.ps1 -arg1 'Hello|-|World\' -arg2 'random|-|dwua0928]43d}'"
For a comprehensive overview of the PowerShell CLI, see this post.
As for what you tried:
You called powershell.exe with neither -File (-f) nor -Command (-c), which defaults to -Command (note that, by contrast, pwsh, the PowerShell (Core) 7+ CLI, defaults to -File).
When -Command is used, PowerShell's command-line processing first strips all subsequent arguments of any syntactic (i.e., unescaped) " characters, namely from "..."-enclosed arguments, joins the resulting tokens with spaces, and then interprets the resulting string as PowerShell code.
In your case, the fact that you used '...' quoting around the arguments is irrelevant:
PowerShell invariably translates the original quoting into "..." quoting when calling external programs (as they can only be expected to "..." quoting, not also '...' quoting)
However, it only uses "..." quoting if needed, which is solely based on whether the argument at hand contains spaces.
Neither of your arguments, verbatim Hello|-|World and random|-|dwua0928]43d}, contained spaces, so they were placed as-is on the command line for powershell.exe (though note that even if they did contain spaces and had been enclosed in "..." as a result, PowerShell's command-line processing would have stripped the " chars.)
The net result was that the powershell.exe child process tried to execute the following PowerShell code, which predictably breaks with the errors shown in your question:
# !! Note the absence of quoting around the -arg1 and -arg2 values.
.\script.ps1 -arg1 Hello|-|World -arg2 random|-|dwua0928]43d}
You can easily provoke the error you saw as follows from a PowerShell session:
# -> Error "Missing expression after unary operator '-'."
Write-Output Hello|-|World
I have a declarative pipeline that should execute a powershell step for mounting a path.
The path is provided as a parameter:
parameters {
string(name: 'UNC', defaultValue: '\\\\server01.lab.local\\shared_data', description: 'Shared location to build-data')
}
When I use this value in a simple print, it is working as expected:
...
powershell('''
write-host "test: "${env:UNC}""
''')
...
So the next step was actually to mount it. However, it is not working as expected:
...
powershell('''
New-PSDrive -Name "k" -PSProvider "FileSystem" -Root "\"${env:UNC}\"\"
''')
...
The error that I'm getting here is: powershell.exe : New-PSDrive : A positional parameter cannot be found that accepts argument '\\server01.lab.local\shared_data'. New-PSDrive -Name "k" -PSProvider "FileSystem" -Root ""${ ...
This is when I understand that quoting is the issue. The Powershell command that does work is:
New-PSDrive -Name "k" -PSProvider "FileSystem" -Root "\\server01.labl.local\shared_data"
So what am I missing here in terms of escaping the quotes in the UNC path?
Thanks
Do not try to quote ${env:UNC} at all:
powershell('''
New-PSDrive -Name "k" -PSProvider "FileSystem" -Root ${env:UNC}
''')
In cases where you do need to escape " inside "...", use `" not \"; e.g.,
"Nat `"King`" Cole" yields verbatim Nat "King" Cole - read on for more information.
Inside PowerShell, \ has no special meaning and instead it is ` (the so-called backtick) that serves as the escape character, namely in these contexts:
Inside double-quoted strings ("...")
Inside "..." only, you can use "" as an alternative to `" for escaping embedded " chars (but the other two characters that require escaping inside "...", $ and ` itself, can only be escaped as `$ and ``, respectively).
In unquoted command arguments (not often seen); e.g.:
Get-Item C:\Program` Files # Note the escaped space char.
Note that ` isn't just used to signal that the next character is to be used verbatim, but also as the start of escape sequences representing control characters and, in PowerShell (Core) 6+ only, Unicode characters; e.g., "`t" expands to a tab character, and "`u{fc}" to ü. See the conceptual about_Special_Characters topic.
Double-quoting variable references (or expression results) used as command arguments is never necessary in PowerShell (unless you explicitly need to convert to a string first).
E.g., the following works just fine:
$dir = 'C:\Program Files'
Get-Item $dir # NO need to double-quote $dir
If you really need to pass verbatim " chars. as part of an argument's actual value:
Write-Output "Nat `"King`" Cole"
Note: Even when calling external executables double-quoting around variable references / expression isn't needed, because PowerShell then automatically applies double-quoting behind the scenes, as needed, based on whether a value contains spaces or not.
Unfortunately, passing arguments with verbatim embedded " characters to external programs is still broken as of PowerShell 7.1, though a fix is finally being considered - see this answer.
As for what you tried:
\ is not special in PowerShell (though it is needed to escape verbatim " chars. in arguments to the PowerShell CLI[1]).
Therefore, "\"${env:UNC}\"\" is parsed as follows:
Argument 1:
"\" is a double-quoted string with verbatim content \. Since a quoted string at the start of a compound token (i.e. if directly followed by another quoted or unquoted token) is always considered an argument by itself, this value becomes its own argument. This notable pitfall is discussed in this answer.
Argument 2:
${env:UNC}\ expands to the value of environment variable UNC followed by verbatim \
"\" is again a double-quoted string with verbatim content \; because it directly follows the unquoted token ${env:UNC}\, it is considered part of the same argument.
The following example, which outputs the arguments received enclosed in <...>, each on its own line, demonstrates this:
PS> $env:UNC='foo bar';
& { foreach ($arg in $args) { "<$arg>" } } "\"${env:UNC}\"\"
<\>
<foo bar\\>
[1] This applies to powershell.exe, the Windows PowerShell CLI; pwsh, the PowerShell (Core) v6+ CLI, alternatively accepts ""- see this answer for more information.
Been fighting with Powershell to do what I need it to do. It might be a simple solution but I've not found it.
If this question is a duplicate, I do apologize, but I couldn't find the answer I was looking for.
TL;DR at the bottom.
So, to the issue I have.
I'm trying to add a PS script to context menu via regedit that deletes a folder + files within, it works great on folders without any spaces in it but when I try to delete a folder with spaces (like "New Folder") it throws an error and closes.
(Tried looking for a pause / sleep command with Remove-Item but no luck, except for long scripts with error handling etc.)
I suspect the error is similar to
Remove-Item : A positional parameter cannot be found that accepts argument 'Folder'.
At line:1 char:12
+ Remove-item <<<< New Folder
+ CategoryInfo : InvalidArgument: (:) [Remove-Item], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
And the code I'm currently using is
cmd /c PowerShell Remove-Item "%1" -recurse -force
I've tried different variations for it to work without luck.
I.E replacing "%1" with "pathAsString", added another "% 1", added wildcard "* *", removed the flags.
Some different variations on the code I've tried:
cmd /c PowerShell Remove-Item \"%1\" -recurse -force
cmd /c PowerShell Remove-Item & "{"%1" -recurse -force"}"
TL;DR
cmd /c PowerShell Remove-Item "%1" -recurse -force ignores folders with white spaces, tried various things.
Usage in Context Menu (via Regedit).
Might be a solution that's obvious but I don't see it.
The following worked for me:
cmd /c PowerShell -command "& {Remove-Item -path '%1' -recurse -force -confirm:$false}"
When using cmd.exe, there is no such thing as a script block. This means the value passed to -command will always be a string. I added the single quotes ('') around %1 to keep your double quotes ("") with your argument. Apparently, this method will fail if you have a single quote (') in your file path. Also the call operator & is not required even though the online documentation says it is.
See PowerShell.exe Command-Line Help for more information on how to use PowerShell.exe.
See About Quoting Rules for more information on how PowerShell single and double quotation marks work.
One of your attempts:
cmd /c PowerShell Remove-Item \"%1\" -recurse -force
actually does work, albeit only if those paths that have embedded spaces don't contain multiple adjacent spaces (see the bottom section for why your first attempt with "%1" instead of \"%1\" didn't work).
To make the command robust (and also more efficient), use the following variation, which does not involve cmd.exe (because it isn't necessary), and the command to pass to PowerShell is enclosed in "..." as a whole with embedded " escaped as \" (as in your attempt):
powershell.exe -noprofile -command "Remove-Item -LiteralPath \"%1\" -Recurse -Force"
PowerShell CLI options used:
-noprofile suppresses loading of the current user's profile ($PROFILE), which would only unnecessarily slow down the command (or, worse, could change its behavior).
-command tells PowerShell that the remaining arguments are a PowerShell command (a piece of source code, as distinct from -file, which is used to invoke a script)
While you don't strictly need to specify -command in Windows PowerShell, because it is implied, that is no longer the case in PowerShell Core, which defaults to -file.
The Remove-Item command passed to PowerShell:
Enclosing the entire PowerShell command in "..." ensures that whitespace inside the command is preserved as-is, which matters with paths that happen to contain runs of multiple spaces, e.g. "c:\Users\jdoe\Invoices<space><space>2018"
\" is how PowerShell requires nested (embedded) " chars. with to be escaped when called from the command line with -command - see below for an explanation.
-LiteralPath ensures that Remove-Item interprets the path literally rather than as a wildcard pattern (as would happen by default, with the implied -Path parameter); while not very likely, paths such as c:\tmp\folder[] would break the command if taken as a wildcard.
Caveats:
Due to (ultimately) using double quotes ("...") to enclose the path (the directory path that File Explorer expands %1 to), it becomes an expandable string, i.e., it is subject to string interpolation, which means that folder paths that contain $ characters (e.g., c:\tmp\$foo) could be misinterpreted.
You can suppress this interpolation by replacing \"%1\" with '%1', i.e., by using a single-quoted string (which PowerShell always treats literally), but the problem is that you then won't be able to use the command on folders whose paths happen to have ' chars. in them (e.g., c:\tmp\o'reilly)
Why just "%1" isn't enough:
cmd /c PowerShell Remove-Item "%1" -recurse -force, your (first) command line (which you're using as a command definition for File Explorer's context menus), is processed as follows:
File Explorer replaces %1 with the absolute path of the file for folder that was right-clicked, as-is - whether or not the path has embedded spaces; e.g.:
c:\Users\jdoe\Invoices 2018.
The resulting command line is then executed; e.g.:
cmd /c PowerShell Remove-Item "c:\Users\jdoe\Invoices 2018" -recurse -force
cmd /c is incidental here (and not needed): It essentially just relays the command line to PowerShell (via its executable, powershell.exe); e.g.:
powershell.exe Remove-Item "c:\Users\jdoe\Invoices 2018" -recurse -force
PowerShell first processes the individual arguments - Remove-Item, "c:\Users\jdoe\Invoices 2018", -recurse, -force by removing enclosing double quotes.
Since the -command option is implied, the quote-stripped arguments are re-joined with a space as the separator and then interpreted as a snippet of PowerShell source code; e.g.:
Remove-Item c:\Users\jdoe\Invoices 2018 -recurse -force
As you can see, the removal of the double quotes resulted in a broken PowerShell command, because the path with spaces now lacks quoting so that c:\Users\jdoe\Invoices 2018 is no longer recognized as a single argument and is instead interpreted as 2 arguments, prefix c:\Users\jdoe\Invoices followed by 2018 as its own, syntactically extraneous argument.
While using \"%1\" instead of "%1" alone would prevent the up-front quote stripping - by telling PowerShell that the " chars. are to be retained during initial argument parsing - additionally enclosing the entire PowerShell command in "..." is necessary to correctly preserve file paths with multiple adjacent spaces (even though such paths may be the result of a typo during folder creation):
Without overall enclosing "...", a \"-quoted path such as C:\Users\jdoe\Invoices<space><space>2018 would result in the following 2 arguments:
\"C:\Users\jdoe\Invoices
2018\"
When PowerShell later re-joins the individual arguments with a single space between them (after having recognized \" as an escaped " to be retained), before interpreting the resulting string as PowerShell code, it sees "C:\Users\jdoe\Invoices<space>2018", i.e., only a single space.
With overall enclosing "...", the entire PowerShell command is parsed as a single argument, with interior whitespace preserved as-is, which avoids the problem.
PS C:\> cd Program Files
When I give this command, I don't know why, but it is not accepting Program Files. The same command is working perfectly fine in cmd.
This is the error it shows:
Set-Location : A positional parameter cannot be found that accepts argument 'Files'.
At line:1 char:1
+ cd Program Files
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Location], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
tl;dr
Since your directory name contains spaces, you must quote it, e.g.:
# Note: In PowerShell, 'cd' is an alias of 'Set-Location'
cd 'Program Files'
As stated in Maximilian Burszley's helpful answer, Program Files is parsed as two arguments, because spaces are used to separate command-line arguments.
Your attempt to use cd Program Files may be inspired by cmd.exe (the legacy Command Prompt), where this syntax indeed works; however, even there it conceptually violates the usual rules of argument parsing.
Therefore, you need to use a form of quoting in order to pass a value that contains spaces as a single argument.
You have several options to implement this quoting:
Use '...' around values that are literals, i.e., that should be used verbatim; a '...' string is called a verbatim (single-quoted) string.
Use "..." around values in which you want to embed variable references (e.g., $HOME) or subexpressions (e.g., $(Get-Date); that is, "..." strings perform string interpolation, and they're called expandable (double-quoted) strings.
Use ` to quote (escape) a single character; `, the so-called backtick, is PowerShell's general escape character.
Therefore, you could use any of the following:
cd 'Program Files'
cd "Program Files" # as there are no $-prefixed tokens, same as 'Program Files'
cd Program` Files # `-escape just the space char.
Also, you can use tab-completion to expand the (space-less) prefix of a space-containing path to its full form with quoting applied implicitly.
E.g., if you're in C:\ and you type:
cd Program<tab>
PowerShell automatically completes the command to:
cd '.\Program Files\'
Note how the Program Files (along with .\ to refer to the current dir. and a trailing \ to indicate a dir.) was automatically single-quoted.
Using wildcard expressions as arguments:
As noted, in PowerShell cd is a built-in alias of the Set-Location cmdlet.
Passing a path positionally - e.g. Set-Location 'C:\Program Files' - implicitly binds it to the -Path parameter; that is it, is equivalent to Set-Location -Path 'C:\Program Files'
-Path interprets its argument as a wildcard expression, so that you can do something like Set-Location C:\Win* in order to change to C:\Windows (assuming that the wildcard expression matches only one directory).
The tricky thing is that - unlike in cmd.exe - it isn't just * and ? that have special meaning in PowerShell wildcard expressions, but [ and ] as well (for character-set and character-range expressions such as [abc] and [a-c]), so that Set-Location Foo[1] will not work for changing to a directory literally named Foo[1].
In that case, you must use the -LiteralPath parameter -
Set-Location -LiteralPath Foo[1] - to ensure that the path is interpreted literally (verbatim).
cd C:\Program` Files\
` just escape the space in folder name.
The reason is invalid syntax. Each argument to a powershell command is separated by space, so what you're actually doing is something similar to:
Set-Location -Path Program -Arg2 Files
But Set-Location (aliased: cd) does not have any positional arguments for a second position, so it can't bind it to any parameters and use it in the command, hence the terminating error.
If you want a simpler cd alias, you could do something like this (in your $profile):
function ChangeDirectory {
param(
[Parameter(
Position = 0,
Mandatory,
ValueFromRemainingArguments
)]
[string[]] $Path
)
Set-Location -Path ($Path -join ' ')
}
Set-Alias -Name cd -Value ChangeDirectory
Do note, however, that if you're not specifying a relative path (.\), it will use the root path of your current drive (most likely, C:\). This can be tuned in the function to test for both locations (relative and drive-rooted), but logic for figuring out which one to use if they both exist would be tricky (or can always default to relative).
Use quotes (double or single) to go to the folder or path contains white spaces or special symbols.
Forexample:-
cd "C:\Users\LAPTOP0534\OneDrive - My Content (Active Directory)"
OR
cd 'OneDrive - My Content (Active Directory)\My Folder'
For your case it will be PS C:\> cd "Program Files"
Just run your command prompt as administrator .
See the picture attached.
It will run easily
This is because of the space between the sentences. Enter code here You complete the tab.
Replace PS F: > cd new folder with PS F: > cd "new folder" in the PowerShell. Or look at F: > cd './new folder'. I'm a beginner and you should try this.
There are a couple questions related to this on here but they specifically address Write-Host. I want to run something like
powershell.exe -Command "'example.exe' /f`"`{GUID`}`""
Only it fails with the error
Missing closing '}' in statement block.
At line:1 char:396
+ $mypid=(get-process EXEName*).id;wait-process -id $mypid;
& `C:\Users\User\AppData\Local\Temp\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\Target.exe`
/s /ig``{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}` /instance=1 /f3`C:\Recordings`
/f4`uninstall-log.txt` /f1`C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}\setup.iss`
/f2`C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}\setup.log` /removeonly <<<<
+ CategoryInfo : ParserError: (CloseBraceToken:TokenId) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
I have other strings starting with " and containing {} (like /f"C:\Folder\{GUID}\program.exe" and these don't cause any trouble. It's only my argument where the curly braces are adjacent to the double quotes:
/ig`"`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}`"
In the error message, it may or may not be noteworthy that all the double quotes are gone so my /ig argument is left with two backticks. I believe my version is 2.0. Here is my actual (modified) command:
powershell -Command "$mypid=(get-process EXEName*).id;wait-process -id
$mypid;& 'C:\Users\User\AppData\Local\Temp\{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX}\Target.exe' /s /ig`"`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}`" /instance=1 /f3`"C:\Recordings`" /f4`"uninstall-log.txt`"
/f1`"C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}\setup.iss`"
/f2`"C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}\setup.log`" /removeonly"
Can anyone shed some light on this? I don't know why it would be invalid. Thank you!
The problem is using the -Command parameter for anything other than a simple script you will run into issues where characters such as curly braces and quotes will be misinterpritted by the command prompt before the are they are passed to Powershell. You could could tie yourself in knots by adding several layers of escaping or there is a simplier way - use the -EncodedCommand parametter instead.
For the EncodedCommand you just need to Base64 encode your command which you can do with the following Powershell script:
$command = #'
# Enter your commands containg curly braces and quotes here
# As long as you like
'#
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes) | clip.exe
This will copy the encoded command to the clipboard, then all you need to do to use your command is type:
powershell.exe -EncodedCommand
.. and then paste in your command so that you end up with something like the following:
powershell.exe -EncodedCommand IAAgACMAIABFAG4AdABlAHIAIAB5AG8AdQByACAAcwBjAHIAaQBwAHQAIABjAG8AbgB0AGEAaQBuAGcAIABjAHUAcgBsAHkAIABiAHIAYQBjAGUAcwAgAGEAbgBkACAAcQB1AG8AdABlAHMACgAgACAAIwAgAEEAcwAgAGwAbwBuAGcAIABhAHMAIAB5AG8AdQAgAGwAaQBrAGUAIAAgAA==
You now have something that is command prompt safe.