I was building a Windows Service that needs to periodically execute PowerShell scripts (*.ps1 -Option Value
etc) via C#'s Process
and I found this answer here about how to do this, but the example there uses two pairs of quotes (first double "
then single '
) for the Arguments
which is quite confusing:
process.StartInfo.Arguments = "\"&'"+strCmdText+"'\"";
Are they both really necessary and if yes then why?
To offer a more PowerShell-focused answer:
the example there uses two pairs of quotes (first double
"
then single'
) for the Arguments (...) Are they both really necessary and if yes then why?
The quoting requirements depend on whether the -File
or the -Command
parameter of the PowerShell CLI (powershell.exe
for Windows PowerShell, pwsh
for PowerShell (Core) 7+) is used.
In the absence of either parameter, powershell.exe
assumes -Command
, pwsh.exe
assumes -File
.
-Command
allows you to pass arbitrary PowerShell commands, with the code optionally spread across multiple arguments.
"
characters are stripped during the initial command-line parsing, but '
characters are passed through as-is."
as part of the PowerShell code to execute require escaping them as \"
(sic; even though PowerShell-internally `"
must be used).-File
expects the path of a script file (*.ps1
) to execute, with any additional arguments being passed as literal values to that script.
"
quoting can be used with syntactic function - any '
characters are used verbatim.Fundamentally - with both -File
and -Command
- a script-file path only needs quoting if it contains metacharacters, notably spaces.
When quoting is needed, the approaches differ depending on -Command
vs. -File
:
-File
:
Use embedded "..."
quoting around the file path (and around any pass-through arguments that need quoting) - escaped as ""...""
for the sake of embedding in the C# verbatim string:
var scriptPath = @"C:\path with spaces\script.ps1";
process.StartInfo.Arguments = $@"-File ""{scriptPath}""";
-Command
:
While embedded '...'
quoting, as in the question, could be used, doing so isn't fully robust, unless extra action is taken: '
is a legal character in file names, so a path containing '
would break the command (which could be avoided with extra effort; see the edge case below).
Using embedded "..."
quoting avoids this problem, given that "
isn't valid in file names (at least on Windows). For the reasons stated above, this requires \
-escaping the "
characters (spaces added for readability):
var scriptPath = @"C:\path with spaces\script.ps1";
process.StartInfo.Arguments = $@" -Command "" & \""{scriptPath}\"" "" ";
Note:
If the only operation you're performing is calling a script with literal arguments, there is no reason to use -Command
- use -File
instead.
It is a fundamental syntax requirement in PowerShell (which does't apply to -File
) that a quoted command must be called via &
, the call operator.
The code to execute is passed as a single argument, enclosed in "..."
to -Command
. This isn't strictly necessary in an invocation that doesn't involve a shell, but avoids whitespace normalization that would result from PowerShell "stitching" multiple arguments back together to form the code to execute.
There's a - largely hypothetical - edge case (which does not affect the -File
parameter): If you had file paths that contain verbatim tokens such as $foo
, they would be subject to - unwanted - string interpolation by PowerShell; in that case, embedded '...'
quoting must be used instead, but with any embedded '
chars. escaped as ''
:
var scriptPath = @"C:\path with spaces\script.ps1";
process.StartInfo.Arguments = $@" -Command "" & '{scriptPath.Replace("'", "''")}' "" ";