【问题标题】:How to execute powershell script from FAKE - F#MAKE如何从 FAKE 执行 powershell 脚本 - F#MAKE
【发布时间】:2017-03-31 11:38:38
【问题描述】:

我正在将 MSBuild 代码迁移到 FAKE - 我无法从 fake 执行 powershell 脚本,下面是用 FAKE 编写的 MSBuild 文件的代码:

<Target Name="VersionConfig"> 
    <Exec IgnoreExitCode="false" LogStandardErrorAsError="true" StandardErrorImportance="high" Command="powershell -File &quot;$(BuildRoot)\DeployScripts\scripts\AdminScripts\VersionUpdateFile.ps1&quot;  -path &quot;$(BuildSolutionVersioningConfig)&quot; -majorVersion &quot;$(BuildNumberMajor)&quot; -minor &quot;$(BuildNumberMinor)&quot; -build &quot;$(BuildNumber)&quot; -revision &quot;$(BuildNumberRevision)&quot;"/>
</Target>

如何在 FAKE 中写这个,我是 FAKE 新手,没有太多使用 F#,如果这很明显,请原谅我。

如果有人可以提供帮助,那将是非常有帮助的。

谢谢。

【问题讨论】:

  • 你想做什么? FAKE 有自己的助手修改和assembly information and versions。你检查过Generating AssemblyInfo files 部分吗?
  • 感谢您的帮助,但实际上我对所有这些东西都很陌生,现在作为实习生,我正在努力。所以,我很困惑当我使用 CreateCSharpAssemblyInfo 更改 buildinfo.cs 时是否更新 version.config 文件,或者我是否需要更改 version.config,因为在 MSBuild 文件中,version.config 文件正在由这个 powershell 脚本更新。跨度>

标签: f# f#-interactive f#-3.0 f#-data f#-fake


【解决方案1】:

您可以使用System.Management.Automation 命名空间的类在任何.NET 应用程序中创建Powershell pipeline。您可以使用该管道在本地或远程计算机上执行命令。

以下目标检索进程列表并打印它们。它是 PowerShell 类的文档示例的 F# 等价物:

Target "ProcTest" (fun _ ->
    PowerShell.Create()    
            .AddCommand("get-process")
            .Invoke()
            |> Seq.map(fun result->(  result.Properties.["ProcessName"].Value,
                                      result.Properties.["ID"].Value))
            |> Seq.iter (fun (name,id)->printfn "%-24O %O" name id)
)

这是一个快速而肮脏的模块,我用来在远程服务器上执行命令(特别是巧克力命令)。要使其在本地机器上运行,您唯一需要做的就是删除 ComputerName 参数:

#r "System.Management.Automation"

module MyPowershell =

    let InvokeRemote server command =
        let block = ScriptBlock.Create(command)
        let pipe=PowerShell.Create()    
                .AddCommand("invoke-command")
        pipe.AddParameter("ComputerName", server)            
            .AddParameter("ScriptBlock", block)
            .Invoke() 
            |> Seq.map (sprintf  "%O")
            |> Seq.iter (fun line ->
                                let tracer=if line.Contains("not installed") then
                                                traceError 
                                        else 
                                                trace
                                tracer line)
        pipe.Streams.Error |> Seq.iter (traceError << sprintf "%O" )

管道由PowerShell 类表示。构建它的步骤是:

  1. 使用PowerShell.Create() 创建一个 PowerShell 管道
  2. 使用要执行的命令创建一个 ScriptBlock。您不需要所有命令的 ScriptBlock。在这种情况下,ScriptBlock 包含我要远程执行的脚本。
  3. 添加您要执行的内容。就我而言,我想运行一个 invoke-command 命令
  4. 添加参数。在这种情况下,它是 -ComputerName myServer-ScriptBlock ... 与要执行的块。
  5. 使用PowerShell.Invoke() 运行管道
  6. 解析结果以查找未进入错误流的可能警告消息

PowerShell 错误被发送到Error 流,这使得错误处理更容易。

在本地机器上执行命令会简单很多:

let InvokeRemote command =
    let pipe=PowerShell.Create()    
            .AddCommand(command)
    pipe.Invoke() 
        |> Seq.map (sprintf  "%O")
        |> Seq.iter (fun line ->
                            let tracer=if line.Contains("not installed") then
                                            traceError 
                                    else 
                                            trace
                            tracer line)
    pipe.Streams.Error |> Seq.iter (traceError << sprintf "%O" )

【讨论】:

    【解决方案2】:

    FAKE 中的Fake.ProcessHelper namespace 就是您要查找的内容。文档不会告诉您这一点,但 Fake.ProcessHelpermarked with the AutoOpen attribute,这意味着我链接的 API 参考页面中列出的所有函数都可以从您的 FAKE 构建脚本中获得,而您不需要任何明确的 open语句来使用它们。你可以这样使用它:

    let inQuotes s = sprintf "\"%s\"" s
    
    Target "Sample" (fun _ ->
      let retCode =
        ExecProcess
          (fun info ->
            info.Name <- "powershell.exe"  // Don't know if you need full path here
            info.WorkingDirectory <- getBuildParam "BuildRoot"
            info.Arguments <-
              [ "-File"; getBuildParam "BuildRoot" + "\DeployScripts\scripts\AdminScripts\VersionUpdateFile.ps1" |> inQuotes;
                "-path"; getBuildParam "BuildSolutionVersioningConfig" |> inQuotes;
                "-majorVersion"; getBuildParam "BuildNumberMajor" |> inQuotes;
                "-minor"; getBuildParam "BuildNumberMinor" |> inQuotes;
                "-build"; getBuildParam "BuildNumber" |> inQuotes;
                "-revision"; getBuildParam "BuildNumberRevision" |> inQuotes
              ] |> separated " "
          )
          (TimeSpan.FromMinutes 5.0)
      if retCode <> 0 then
        failwith (sprintf "PowerShell exited with non-zero exit code %d" retCode)
    )
    

    几个注意事项:

    【讨论】:

    • 这将像外部进程一样运行 Powershell。可以在代码中创建 Powershell 管道
    • @PanagiotisKanavos - 好点。对于 Powershell,最好查看那个脚本,看看它做了什么,然后把它变成一个合适的管道。在这个脚本的情况下,你关于"Generating AssemblyInfo files" documentation 的观点也很好:John Fernandes 可能应该将此特定脚本转换为使用CreateCSharpAssemblyInfo但是...作为一般规则,如果您想调用外部进程并且还没有 FAKE 函数可以帮助您,那么您需要使用 ProcessHelper 函数。跨度>
    • PowerShell 虽然不是外部进程。您可以在自己的进程中托管它。事实上,它的工作方式几乎类似于 F# 管道,每个命令行开关都读取和生成对象。每个.AddCommand 都像|&gt; Seq.... 一样工作。通过读取整个文件并使用AddScript(scriptText),也可以执行脚本文件。
    猜你喜欢
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-03
    • 2019-08-18
    • 1970-01-01
    相关资源
    最近更新 更多