What is up with this PowerShell command line quoting/escaping?
Asked Answered
H

3

12

I obviously don't know what I'm doing.

I have finally got this PowerShell command to work. But I can't figure out why it works.

My concern is the final "" characters:

    &"C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.exe" `
    -verb:sync `
    -source:contentPath="$build_directory\deploy" `
    -dest:contentPath="$server_temp_directory,computerName=$server,username=$server_username,password=$server_password" `
    -verbose `
    -postSync=runCommand="powershell -NoLogo -NoProfile -Command $server_temp_directory\remotetasks.ps1 Deploy""

Why do I need double double-quotes?

My IDE (PowerGUI) says the line is not ended correctly, but it is the only way I can make the command run as wanted.

What is it, that I - and the IDE - don't know about quoting in PowerShell?


A little output from echoargs:

If I run:

echoargs -postSync=runCommand="powershell -NoLogo -NoProfile -Command $server_temp_directory\remotetasks.ps1 Deploy""

I get:

Arg 0 is <-postSync=runCommand=powershell -NoLogo -NoProfile -Command \remotetasks.ps1 Deploy>

If I run without the double double-quotes, I get:

Arg 0 is <-postSync=runCommand=powershell>
Arg 1 is <-NoLogo>
Arg 2 is <-NoProfile>
Arg 3 is <-Command>
Arg 4 is <\remotetasks.ps1>
Arg 5 is <Deploy>

Another thing to notice is that the above command does only work if it uses = instead of : in the last argument.

This won't work:

-postSync:runCommand="powershell -NoLogo -NoProfile -Command $server_temp_directory\remotetasks.ps1 Deploy""

I have tried the array solution like this:

$arguments = @("-verb:sync",
               "-source:contentPath=$build_directory\deploy",
               "-dest:contentPath=$server_temp_directory,computerName=$server,username=$server_username,password=$server_password",
               "-verbose",
               "-postSyncOnSuccess:runCommand=powershell -Command $server_temp_directory\remotetasks.ps1 Deploy")
&"C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.exe" $arguments

I still get the same error:

Error: Unrecognized argument '"-postSyncOnSuccess:runCommand=powershell -Command c:\temp\kslog\remotetasks.ps1 Deploy"'. All arguments must begin with "-".

Am I doing some new thing wrong here?

Honeysuckle answered 8/10, 2010 at 11:53 Comment(0)
L
11

This is a notorious issue. The ticket “Executing commands which require quotes and variables is practically impossible” is the most voted bug: https://connect.microsoft.com/PowerShell/Feedback

You can find there a few workarounds as well. But I would recommend you to compose all the parameters as an array and use the & operator to invoke a native command with this array. See the answers and examples: Running an EXE file using PowerShell from a directory with spaces in it and Executing a Command stored in a Variable from Powershell

I did not work with msdeploy.exe and cannot provide some demo code for your case. But I applied this approach to many other tricky native commands well enough. Please, try it and let us know the results.

P.S. Technically this is not exactly an answer to your questions but I assume you are still looking for a practical way of doing this, so it still might be helpful.

Legator answered 8/10, 2010 at 12:28 Comment(7)
Thank you for the answer! I'm away from the code this weekend, but I will try out your suggestions when I get back monday. You're right, that this is technically not an answer to why the double double-qoutes works... but if it is a bug, and not by design, then it might not be worth getting a deeper understanding of anyway. I'll return shortly.Honeysuckle
So. I have tried all sorts of combinations of using an array for the parameters. Nothing seems to work. Do you have an idea why?Honeysuckle
I do not use msdeploy.exe and cannot try it myself. Can you now try the last argument with two extra escaped quotation marks: "-postSyncOnSuccess:runCommand=<backtick>"powershell -Command $server_temp_directory\remotetasks.ps1 Deploy<backtick>"" (<backtick> is for backticks, to avoid SO formatting)Legator
Thank you for the suggestion! Unfortunately it does not make a difference. I still get the same error message. Well, in fact it does make a tiny difference: When doing it with the escaped qoute marks the echoargs command returns 8 arguments, when doing it without it actually return only 5. None of them work when executed, but the difference is somewhat strage... See gist.github.com/623756Honeysuckle
Weird... I am about to give up. BTW, just in case, I have taken a look at MSDN for msdeploy.exe syntax. There is no postSyncOnSuccess there, I can see only postSync. Can this be a reason? That would explain the error message about “Unrecognized argument”. technet.microsoft.com/en-us/library/dd569089(WS.10).aspxLegator
Thank you very much for your patience! It still doesn't work, even if I use only the documented syntax (the other one is actually available too). But I give up now too :)Honeysuckle
Here we are in 2013 and powershell and msdeploy still cannot cope. I had the same problem and after much hair pulling ended up calling msdeploy.exe from a batch file (which kinda defeats the purpose of powershell). I wish MS would address this.Lorineloriner
L
3

I finally found out how to do this. Here is a sample script:

$src = 'C:\sandbox\Test Folder\A'
$dest = 'C:\sandbox\Test Folder\B'
$msdeploy = Get-Command 'C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe'
$command = "& `$msdeploy --% -verb:sync -source:contentPath=""$src"" -dest:contentPath=""$dest"""
$sb = $ExecutionContext.InvokeCommand.NewScriptBlock($command)
& $sb
Lorineloriner answered 8/8, 2013 at 15:6 Comment(0)
A
1

One of the areas at fault here is the inability of PowerShell to cope with the translation of the whitespace between "Program Files" expressed in the cmd-based msdeploy.

This is an extremely basic solution and leverages antiquated DOS limitations, but the following non-whitespace references can be used in their stead:

\Program Files\ ... \Progra~1\
\Program Files (x86)\ ... \Progra~2\

It's lame, but it works.

You can also create a function that calls cmd and executes your command. This example also assumes what you're trying to call (msdeploy, etc.) is in the path.

function PushToTarget() {
     cmd.exe /C $("msdeploy.exe -verb:sync -source:apphostconfig=yourwebapp-dev -dest:archivedir=d:\backup")
}
Arlinearlington answered 13/2, 2013 at 20:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.