1

I have a script like this:

param(
[string]$root,
[string]$bin,
[string]$out,
[string]$zdir
)


echo "args..."
echo "Root: $root", "zdir: $zdir", "out: $out", "bin: $bin"

I invoke it like follows:

powershell.exe -nologo -noprofile -file "C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\zip.ps1" -root "C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\" -zdir "Zip" -out "output.zip" -bin "C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\bin\Debug\" 

But my output is quite to the contrary:

C:\code\misc>powershell.exe -nologo -noprofile -file "C:\Users\arun_jayapal\Docu
ments\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\zip.ps1" -root "
C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\Outlo
okCompass\" -zdir "Zip" -out "output.zip" -bin "C:\Users\arun_jayapal\Documents\
Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\bin\Debug\"
args...
Root: C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompas
s\OutlookCompass" -zdir Zip -out output.zip -bin C:\Users\arun_jayapal\Document
s\Visual
zdir:
out: 2013\Projects\OutlookCompass\OutlookCompass\bin\Debug"
bin: Studio
deostroll
  • 11,661
  • 21
  • 90
  • 161
  • 1
    the variable `$root` hold the entire should just hold `"C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\"` but thats not whats output... – deostroll Jan 27 '16 at 13:42

2 Answers2

2

Your problem is that you are invoking your powershell script from cmd.exe, and for many legacy .exe programs the sequence \" is used to escape a quote into a parameter. Powershell respects this convention so when you invoke Powershell.exe you have to follow this escaping convention.

So:

-root "something\" -zdir "Zip" -out "output.zip" -bin "something\"

Is a single argument containing two double quotes escaped into the string with \". cmd removes the other double quotes, so Zip and output.zip are strings outside the quotes, but as they don't contain any spaces they don't split the argument.

This should work:

-root "something\\" -zdir "Zip" -out "output.zip" -bin "something\\"

Doubling the backslashes before the quotes mean a single backslash is passed through and the quote mark loses its special meaning. Don't double any of the other backslashes though as cmd only regards the backslash as special when a sequence of one or more of them precede a double quote.

Alternatively leave off the trailing backslashes and insert them where needed in your script.

Or just use powershell itself as your main command prompt and ditch cmd altogether.

BTW, if you invoke another copy of powershell from inside itself it is recognised as something special and the arguments are encoded in base64 and passed with the -EncodedCommand command-line option so in that case there is no need to worry about escaped quotes.

Duncan
  • 92,073
  • 11
  • 122
  • 156
  • I am running this within visual studio...so what do you suggest I do now? – deostroll Jan 27 '16 at 13:44
  • 1
    Are you generating the command line from a program? If so you could perhaps use the `-EncodedCommand` command line argument and pass your parameter string base64 encoded. – Duncan Jan 27 '16 at 13:50
  • 1
    @mklement0, yes, I've tried to improve that part of my answer. The weird treatment of `\"` actually comes from Microsoft's C runtime library so it is common to most `.exe` programs, but it is a convention for such executables not an aspect of `cmd.exe` itself. – Duncan Jan 27 '16 at 14:10
  • Good to know about the implicit use of `-EncodedCommand` when invoking another PowerShell instance from PowerShell - handy stuff. Regarding who does the interpretation: I also suggest revising `*cmd* only regards the backslash as special` - again it is _PowerShell_ (possibly via the C runtime library, as you state). On Windows, interpretation of the command line is really completely up to the _program invoked_ - `cmd.exe` does _not_ break the command line into parameters (arguments) up front the way that _Unix_ shells do. – mklement0 Jan 27 '16 at 16:55
0

Update: The problem still affects Windows PowerShell, but has been fixed in PowerShell (Core) 7+.


Duncan's helpful answer contains the crucial pointer (and helpful background information as well): \" at the end of the parameter values is interpreted as an escaped ", which causes parameter boundaries to be misinterpreted.

Your options are:

  • Use \\" instead of \" at the end of each parameter value, as Duncan suggests (e.g., "c:\foo bar\\")
  • Omit the trailing \ from the paths altogether (e.g., "c:\foo bar" instead of "c:\foo bar\")
  • If you want to avoid having to modify the parameter values, use the following, single-quoted solution (verified on v3).

Single-quoted solution:

This assumes that you're calling this from cmd.exe, whether from a batch file or at the command prompt (inside PowerShell, you could just use & <script> ...)

  • use single quotes instead of double quotes
  • use the -Command parameter instead of -File
  • prefix the script path with & (escaped with ^ to have cmd.exe treat it as a literal)
powershell.exe -noprofile -command ^& 'C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\zip.ps1' -root 'C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\' -zdir 'Zip' -out 'output.zip' -bin 'C:\Users\arun_jayapal\Documents\Visual Studio 2013\Projects\OutlookCompass\OutlookCompass\bin\Debug\'
  • This works, because by using -Command (and PowerShell's & (call) operator to invoke a script whose path has embedded spaces), the rest of the command line can be quoted PowerShell-style with single-quoted strings that aren't subject to interpretation (not even by the C-style argument parsing that underlies PowerShell's own startup command-line parsing, with the exception of embedded " chars. - see below).

  • The only characters that need escaping with this approach are:

    • Use '' to embed a literal ' inside a parameter.
    • Use \" to embed a literal " inside a parameter.
    • If you need to escape % chars. to protect them from interpretation by cmd.exe as part of environment-variable references (which you'd have to do irrespective of whether you use single- or double-quoted strings), see this answer of mine.
  • Note that in order to execute a script in the current directory you must .\-prefix the script filename, as you would inside PowerShell.

mklement0
  • 382,024
  • 64
  • 607
  • 775