简体   繁体   中英

How do I escape quotes in an msdeploy post sync command that is executing a powershell script?

I am attempting to setup a Cake task that will use MsDeploy to sync a powershell script to a remote server and then execute that script as a post sync command.

The problem I'm facing is finding a combination of quotes and escapes within the cake file that allow the command to make it all the way to powershell with the file path quoted correctly to allow powershell to find the script; the path has spaces in it.

This appears to be so difficult because of the chain of execution that this command goes through at run time. First because this is written in C# the string either needs to be verbatim ( @"command here" ) or to have all internal double quotes escaped with \\ .

Next Cake performs several operations on the arguments, though none seemed to affect things like quoting and escaping until it actually got to the execution, at which point it uses the C# Process.Start() static method to run the MsDeploy executable. In my reading it was suggested that this method of running a command requires three double quotes to be properly escaped, though that didn't mesh with what I was seeing when I tried.

Then once on the remote machine MsDeploy uses CMD.exe to execute the command which notably has no support for single quotes so double quotes either needed to be escaped by using \\" or "" .

The closest I have gotten looks like this:

Task("InitializeIISApplication")
    .IsDependentOn("InjectVariables") 
    .Does(() => {
        MsDeploy(new MsDeploySettings
        {
            Verb = Operation.Sync,
            RetryAttempts = 3,
            RetryInterval = 10000,
            Source = new FilePathProvider
            {
                Direction = Direction.source,
                Path = MakeAbsolute(File(@".\MyPowershell.ps1")).ToString()        
            },
            Destination = new FilePathProvider
            {
                Direction = Direction.dest,
                Path = File(deployParameters.ApplicationDestinationPath + @"\MyPowershell.ps1").ToString(),
                Username = deployParameters.MsDeployUserName,
                Password = deployParameters.MsDeployUserPassword,
                WebManagementService = deployParameters.DeploymentTargetUrl
            },
            AllowUntrusted = true,
            EnableRules = new List<string> {
                "DoNotDeleteRule"
            },
            PostSyncCommand = new CommandProvider {
                AppendQuotesToPath = false,
                Direction = Direction.dest,
                Path = $"powershell -file '{deployParameters.ApplicationDestinationPath}\\MyPowershell.ps1' ",
            }
        });

        MsDeploy(new MsDeploySettings
        {
            Verb = Operation.Delete,
            Destination = new FilePathProvider
            {
                Direction = Direction.dest,
                Path = File(deployParameters.ApplicationDestinationPath + "\MyPowershell.ps1").ToString(),
                Username = deployParameters.MsDeployUserName,
                Password = deployParameters.MsDeployUserPassword,
                WebManagementService = deployParameters.DeploymentTargetUrl
            },
            AllowUntrusted = true
        });
    });

The task dependency is just setting up the deployParameters object.

Which with Cake's Diagnostic verbosity enabled produces the following command into the logs (new lines added for clarity):

"C:/Program Files/IIS/Microsoft Web Deploy V3/msdeploy.exe"  
-verb:sync   
-source:filePath="Y:/PathToBuildArtifact/Deploy/MyPowershell.ps1"
-dest:filePath="C:/Application - With Spaces/MyPowershell.ps1",wmsvc="https://deploy-server/msdeploy.axd",userName=msdeployuser,password=********
-enableRule:DoNotDeleteRule
-retryAttempts:3
-retryInterval:10000
-allowUntrusted
-postSync:runCommand="powershell -file 'C:\Application - With Spaces\MyPowershell.ps1' "

Then ends with the error:

Warning: Processing -File ''C:/Application' failed: The given path's format is not supported. Specify a valid path for the -File parameter.

Any variant I've tried using double quotes inside the postSync command instead results in this error:

Error: Unrecognized argument '-'.

If it matters this is being done on the Bamboo CI server.

Turns out it was likely the -file argument that was causing some weird parsing behaviour. Calling powershell -help within a command prompt displays the following snippet:

Runs the specified script in the local scope ("dot-sourced"), so that the functions and variables that the script creates are available in the current session. Enter the script file path and any parameters. File must be the last parameter in the command, because all characters typed after the File parameter name are interpreted as the script file path followed by the script parameters.

Which hints that there is some special logic there.

So instead I attempted to execute the file using the call operator ( & ) and after a couple different attempts at quoting I ended up with the following post sync command that ran successfully:

PostSyncCommand = new CommandProvider {
                Direction = Direction.dest,
                Path = $"powershell \"\"& \"\"\"\"{deployParameters.ApplicationDestinationPath}\\MyPowershell.ps1\"\"\"\" \"\"",
            }

Note that everything after powershell is contained within two double quotes the first acts as an escape for the call to msdeploy and strings within must have four quotes the first and third to escape the second and fourth for the call to msdeploy and the second then escaping the fourth for the final call to powershell.

Using PowerShell's -File CLI parameter requires use of " as the - only supported - quote character.

(By contrast, using -Command instructs PowerShell to treat the rest of the command line as if it were PowerShell source code , in which case ' is recognized as a quote character, for strings with literal contents [1] ).

Since your PowerShell command line becomes a double-quoted argument to another command that is ultimately passed to cmd.exe , you must additionally , \\ -escape the " chars.

Therefore, the following should work [ update : but doesn't - see comments]:

Path = $@"powershell -file \""{deployParameters.ApplicationDestinationPath}\MyPowershell.ps1\""",

[1] -File requires a double -quoted script-file argument, and all remaining arguments are treated as literals .
By contrast, -Command (which is the implied option in Windows PowerShell , whereas in PowerShell Core it is now -File ) space-concatenates all remaining arguments and treats the result as PowerShell code , which implies that single -quoted arguments are supported too, with individually double -quoted arguments having their quotes stripped before concatenation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM