PowerShell - 访问脚本参数,从 Windows cmd shell 发送

PowerShell - accessing script parameters, sent from Windows cmd shell

问题

  1. 如何访问发送到 PowerShell 脚本(.ps1 文件)的参数?
  2. 您能否访问参数 A:按名称,B:按位置,C:两者的混合?

上下文

我最近尝试编写一个 PowerShell 脚本 (.ps1),该脚本将从 Windows 批处理文件 (.bat)(或潜在的 cmd shell 或 AutoHotKey script) - 它将参数传递给 .ps1 脚本以供其使用(以显示 toast 通知)。感谢 ss64.com 上的说明,我过去曾使用 $args 来做这种事情,但是由于某种原因我可以通过这种方式访问​​参数(尽管传递参数,$args[0] = '' (空字符串)和 $args.Count = 0) 所以最终不得不删除所有 $args 代码,并将其替换为 Param() 脚本。

我仍然不太清楚为什么,但我认为这是我在尝试编写下一个脚本之前应该弄清楚的事情...

代码示例 1:Args(未命名参数)

ToastNotificationArgs.ps1
-------------------------

Write-Debug "The script has been passed $($args.Count) parameters"
If (!$args[0]) { # Handle first parameter missing }
If (!$args[1]) { # Handle second parameter missing }

Import-Module -Name BurntToast
New-BurntToastNotification -Text "$args[0], $args[1]"

^ 我认为上面的代码是正确的,但正如我所说,由于某种原因我一直在努力访问参数并且无法弄清楚原因。 (如果有人发现我做错了什么,请大声喊叫!)

$args[] 是一种有效的方法吗? 我假设是这样,因为它使用了 ss64.com,但也许我需要注意一些陷阱/限制的?


代码示例 2:Param(命名参数)

ToastNotificationParams.ps1
---------------------------
Param(
    [Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true)] [string]$Title,
    [Parameter(Mandatory=$false, Position=1, ValueFromPipeline=$true)] [string]$Message
)

Import-Module -Name BurntToast
New-BurntToastNotification -Text "$Title, $Message"

^ 这是让我的脚本最终运行的唯一方法。但是,当我传入参数时,我的调用 cmd 脚本按位置发送参数,即 (pwsh.exe -File "ToastNotificationParams.ps1" "This is the title" "Message goes here") 而不是按命名对发送参数。 (不确定这是否是最佳做法,但我的脚本最初打算用于暂时保留它的方式)。


虽然这次 Param() 让我的脚本正常工作(而且我也意识到基于位置的参数的固有危险),但有时可能需要基于位置的方法(例如参数的数量未知) )...

代码示例 3:混合

ToastNotificationMix.ps1
------------------------
Param(
    [Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true)] [string]$Title
)

Import-Module -Name BurntToast
For ( $i = 1; $i -lt $args.count; $i++ ) {
    New-BurntToastNotification -Text "$Title, $args[i]"
}

这样的东西有效吗?..如果无效(或有更好的解决方案),将不胜感激!

提前致谢!

  • automatic $args variable is only available in simple (non-advanced) functions / scripts. A script automatically becomes an advanced one by using the [CmdletBinding()] attribute and/or at least one per-parameter [Parameter()] 属性。

    • 使用$args 允许function/script 接受不限数量的位置 参数,通常代替,但也除了使用显式声明的参数。

    • 但它不允许传递 named 参数(参数以预先声明的目标参数名称为前缀,例如 -Title

  • 为了稳健性,最好使用高级(类似于cmdlet)的函数或脚本;此类功能/脚本:

    • 它们需要显式声明参数
    • 它们除了绑定到已声明参数的参数外,不接受任何参数。
      • 但是,您可以使用 [Parameter(ValueFromRemainingArguments)] 定义一个单一的包罗万象的参数,该参数收集所有不绑定到任何其他预声明参数的位置参数.
  • 显式定义的参数是位置默认,按照它们在param(...)块中声明的顺序.

    • 您可以使用 [CmdletBinding(PositionalBinding=$false)]
    • 关闭此默认设置
    • 然后允许您有选择地启用位置绑定,使用个人的Position属性 [Parameter()] 个属性。
  • 当您通过 PowerShell 的 CLI-File 参数调用 PowerShell 脚本时,调用语法基本上 相同就像从 inside PowerShell 调用脚本时一样;也就是说,您可以传递 named 参数 and/or - 如果支持 - positional arguments.

    • 约束:
      • 参数被视为文字
      • 传递 数组 参数(, 分隔元素) 支持。
    • 如果您确实需要将参数解释为来自 inside PowerShell,请改用 -Command / -c CLI 参数
    • 有关何时使用 -File 与 `-Command 的指导,请参阅

总而言之:

ToastNotificationMix.ps1:

[CmdletBinding(PositionalBinding=$false)]
Param(
    [Parameter(Position=0)] 
    [string]$Title
    ,
    [Parameter(Mandatory, ValueFromRemainingArguments)] 
    [string[]] $Rest
)

Import-Module -Name BurntToast
foreach ($restArg in $Rest) {
   New-BurntToastNotification -Text "$Title, $restArg"
}

然后您可以从 cmd.exe 调用您的脚本,例如(我正在使用 pwsh.exe,PowerShell(核心)CLI;对于 Windows PowerShell,使用 powershell.exe):

仅限位置绑定:

:: "foo" binds to $Title, "bar" to $Rest
pwsh -File ./ToastNotificationMix.ps1 foo bar

命名绑定和位置绑定的混合:

:: "bar" and "baz" both bind to $Rest
pwsh -File ./ToastNotificationMix.ps1 -Title foo bar baz