【问题标题】:How do I escape quotes in an msdeploy post sync command that is executing a powershell script?如何在执行 powershell 脚本的 msdeploy post sync 命令中转义引号?
【发布时间】:2023-03-09 04:03:01
【问题描述】:

我正在尝试设置一个 Cake 任务,该任务将使用 MsDeploy 将 powershell 脚本同步到远程服务器,然后将该脚本作为同步后命令执行。

我面临的问题是在 cake 文件中找到引号和转义符的组合,允许命令一直到 powershell 并正确引用文件路径以允许 powershell 找到脚本;路径中有空格。

由于此命令在运行时要经过的执行链,这似乎非常困难。 首先,因为这是用 C# 编写的,所以字符串要么需要逐字显示 (@"command here"),要么使用 \ 转义所有内部双引号。

Next Cake 对参数执行了多项操作,但在实际执行之前似乎没有任何操作会影响引用和转义等操作,此时它使用 C# Process.Start() 静态方法来运行 MsDeploy 可执行文件。在我的阅读中,有人建议这种运行命令的方法需要正确转义三个双引号,尽管这与我尝试时看到的不符。

然后在远程机器上,MsDeploy 使用CMD.exe 执行命令,该命令明显不支持单引号,因此需要使用\""" 对双引号进行转义。

我得到的最接近的看起来像这样:

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
        });
    });

任务依赖只是设置deployParameters对象。

在启用 Cake 的诊断详细程度的情况下,会在日志中生成以下命令(为清楚起见添加了新行):

"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' "

然后以错误结束:

警告:处理 -File ''C:/Application' 失败:不支持给定路径的格式。为 -File 参数指定一个有效路径。

我尝试在 postSync 命令中使用双引号的任何变体都会导致此错误:

错误:无法识别的参数“-”。

如果重要,这将在 Bamboo CI 服务器上完成。

【问题讨论】:

    标签: c# powershell msdeploy cakebuild


    【解决方案1】:

    事实证明,很可能是 -file 参数导致了一些奇怪的解析行为。 在命令提示符内调用 powershell -help 会显示以下 sn-p:

    在本地范围(“dot-sourced”)中运行指定的脚本,以便 脚本创建的函数和变量在 当前会话。输入脚本文件路径和任何参数。 文件必须是命令中的最后一个参数,因为所有字符 在解释文件参数名称后键入 作为脚本文件路径,后跟脚本参数。

    这暗示了那里有一些特殊的逻辑。

    因此,我尝试使用调用运算符 (&amp;) 执行该文件,经过几次不同的引用尝试后,我最终得到了成功运行的以下同步后命令:

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

    请注意,powershell 之后的所有内容都包含在两个双引号中,第一个用作对 msdeploy 调用的转义,其中的字符串必须在第一个和第三个引号中包含四个引号,以转义第二个和第四个引号以调用 msdeploy 和第二个然后转义第四个以最终调用 powershell。

    【讨论】:

    • 不幸的是,文档中没有提到真正的特殊逻辑(除非您使用-NoExit 保持会话打开,否则关于点源的部分没有实际意义): -File 需要 double 引用的脚本文件参数,并且所有剩余的参数都被视为 literals。相比之下,-Command(隐含的)空格连接所有剩余的参数并将结果 视为 PowerShell 脚本,这意味着也支持 单个 引用的参数(而单独的双引号参数在连接之前将其引号剥离)。
    【解决方案2】:

    使用 PowerShell 的 -File CLI 参数需要使用 " 作为 - 唯一支持的 - 引号字符。

    (相比之下,使用-Command 指示PowerShell 将命令行的其余部分视为PowerShell 源代码,在这种情况下' 识别为引号字符,用于具有文字内容的字符串[1])。

    由于您的 PowerShell 命令行成为 另一个命令的双引号参数,最终传递给 cmd.exe您必须另外,\-转义 " 字符。

    因此,以下应该有效 [更新但无效 - 请参阅 cmets]:

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

    [1] -File 需要 引用的脚本文件参数,所有剩余参数都被视为 文字
    相比之下,-Command(这是 Windows PowerShell 中的 implied 选项,而在 PowerShell Core 中现在是 -File空格连接所有剩余的参数 并且 将结果视为 PowerShell 代码,这意味着也支持 单引号 引用的参数,单独 双引号 - 引用的参数在连接之前剥离

    【讨论】:

    • 我已经尝试过这组引号,但是关于Process.Start()、MsDeploy.exe 或 CMD.exe 中的一个如何处理字符串会阻止它进入 powershell 调用
    • @Phaeze:有趣。 Process.Startcmd.exe 是如何出现的(我对 MSDeploy 一无所知)?两者都不需要额外的引号。
    • Cake 使用Process.Start 来调用 msdeploy。然后 msdeploy 在远程服务器上将命令发送到 cmd.exe 一次,我找不到直接调用 ps 的方法。
    • @Phaeze:感谢您更新您的问题;请看我的更新。
    • @Phaeze:--% 是一个 PowerShell 构造 - 我不希望 msdeploy 理解它。 msdeploy docs 表示支持引用 (似乎包括单引号和双引号)。
    猜你喜欢
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多