【问题标题】:How to pass parameter to a PowerShell script using C#?如何使用 C# 将参数传递给 PowerShell 脚本?
【发布时间】:2020-09-04 21:13:04
【问题描述】:

我正在尝试使用 C# 执行 PowerShell 脚本

我的脚本需要参数才能运行,否则会抛出错误。

这是我的脚本的精简版

param ($url)
if ($url -eq $null) {
    throw "No Url was provided" 
}

write-host "$url was the provided value"

现在,使用C# 我正在尝试执行以下脚本

try {
    var defaultSessionState = InitialSessionState.CreateDefault();
    defaultSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;

    using PowerShell ps = PowerShell.Create(defaultSessionState);

    ps.AddScript(@"d:\test.ps1");
    ps.AddParameter("url", "http://example.com/test/");
    ps.Runspace.SessionStateProxy.SetVariable("ErrorActionPreference", "stop");
} 
catch (Exception e) {

}

但我不断收到No Url was provided 错误。如何正确地将参数传递给我的脚本,以及如何访问它?

【问题讨论】:

  • 将ps.AddScript("d:\test.ps1")更改为ps.AddCommand("d:\test.ps1")
  • [Docs](docs.microsoft.com/en-us/dotnet/api/… addScript 接受一个字符串并执行...认为脚本在powershell(不是文件)中是可执行的

标签: c# powershell


【解决方案1】:

AddScript() 用于执行原始代码 - D:\test.ps1 碰巧 是有效代码,但它更意味着将完整的自包含脚本作为字符串放入。

您需要将脚本文件引用添加为 命令,此时我们可以应用参数 - 为此,请改用 AddCommand():

using (PowerShell ps = PowerShell.Create(defaultSessionState))
{
    ps.AddCommand("d:\test.ps1");
    ps.AddParameter("url", "http://example.com/test/");
    ps.AddParameter("ErrorAction", "Stop");
    // or
    ps.Runspace.SessionStateProxy.SetVariable("ErrorActionPreference", "stop");
}

【讨论】:

    【解决方案2】:

    Mathias R. Jessen's helpful answer 提供了一个有效的解决方案,但让我扩展解释:

    PowerShell SDK 的 .AddScript() 方法 有点名不副实,因为它不执行脚本文件,而是任意一段 PowerShell 代码。也就是说,它需要一个包含整个 PowerShell statements 的字符串,换句话说:脚本文件的 contents - 而不是脚本文件的 path em>.

    鉴于在 PowerShell 中一段可重用代码表示为脚本block,.AddScriptBlock() 之类的名称会更容易混淆(但请注意,您必须不将包含在您调用.AddScript()时要在{ ... }中执行的语句的字符串括起来)。[1]

    Mathias 的回答表明 .AddCommand() 是正确使用的方法,因为 PowerShell 中的 command 是代表可以直接执行的东西的任何东西:别名、函数、cmdlet、外部可执行文件 - 以及脚本文件。


    至于你尝试了什么:

    String d:\test.ps1 被解释为一段 PowerShell 代码(与您在命令行上提交此字符串的方式相同)。该代码恰好调用了一个脚本文件,但这样做没有参数。
    也就是说,你的脚本被调用了,但是没有你通过.AddParameter()添加的参数。

    • 还要注意,如果脚本路径恰好包含空格或嵌入的变量引用,则必须使用 embedded 引用并通过&、call operator 调用结果;例如,
      .AddScript(@"& 'd:\test 1.ps1'"); - 有关详细信息,请参阅this answer 到密切相关的问题。

    但是,请注意,您的 .AddParameter() 调用在技术上仍然有效,但接收参数的是 整个脚本块,而不是其中的(一个)语句 em> - 对d:\test.ps1的调用。[2]

    从技术上讲,可以通过splatting 和automatic $args variable(其中收集所有参数,在没有通过param(...) 块进行显式参数声明):

    ps.AddScript(@"d:\test.ps1 @args");
    

    也就是说,如果您的所有代码只是调用单个脚本文件(带参数),.AddCommand() 解决方案既简单又高效。

    顺便说一句:.AddScript(),与.AddCommand() 不同,不受制于执行机器的 PowerShell execution policy,假设传递的语句不尝试调用脚本文件;也就是说,仅由表达式和命令组成的语句,而不是脚本文件 (*.ps1) 执行,而与生效的执行策略无关,但请注意,某些模块会在导入时自动尝试执行脚本文件。


    [1] .AddScript("..."),以 PowerShell 代码表示,有效地执行以下操作:
    . { ... }
    也就是说,它将字符串解析为script block,并通过.、dot-sourcing operator(稍后调用.Invoke()时)执行。

    [2] 如前所述,您传递给.AddScript() 的内容被解析为script block,并且脚本块(如函数和脚本文件)可以接受参数,无论是位置通过automatic $args variable 和 - 通过param(...) 块显式声明的参数 - 命名。举个简单的例子:.AddScript('Write-Output $args[0]').AddArgument('foo') 等价于. { Write-Output $args[0] } 'foo'

    【讨论】:

    • 很好的答案。将参数传递给脚本,是我们可以依赖的功能吗?或者它只是碰巧起作用?你能指出这个功能的文档吗?很想用,但怕以后坏了。
    • 谢谢,@MonticolaExplorator。是的,您可以依赖此功能。请参阅我刚刚添加到答案中的第二个脚注。
    猜你喜欢
    • 2020-04-04
    • 2015-06-07
    • 2011-08-01
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    相关资源
    最近更新 更多