什么是[cmdletbinding()],它又是如何工作的?

67
根据get-help about_Functions_CmdletBindingAttribute,CmdletBinding属性是函数的属性,使它们像编译的cmdlet一样运行。我们可以在脚本顶部使用它。在这种情况下,该函数是什么?是由PowerShell引擎为其所有输入调用的内部隐式“main”函数吗?关于这个语法:
[CmdletBinding(ConfirmImpact=<String>,
                     DefaultParameterSetName=<String>,
                     HelpURI=<URI>,
                     SupportsPaging=<Boolean>,
                     SupportsShouldProcess=<Boolean>,
                     PositionalBinding=<Boolean>)]

我们在做什么?实例化一个cmdlbinding对象并向其构造函数传递参数列表?这种语法可以在param()中找到-例如:[Parameter(ValueFromPipeline=$true)]。这种语法有特定的名称吗?它能在其他地方找到吗?

最后,作为简单的PowerSheller,我们能否通过设置属性来模仿此功能并修改脚本的行为?


6
我猜这里的文档有些误导:所有这些都与任何类型的脚本块相关,正如Shay所提到的 - 通过函数名命名,通过路径(脚本)命名,但也可以是无名的,例如& {[CmdletBinding()]param() Write-Verbose 'Foo'} -Verbose能够正常工作。 - BartekB
3个回答

24

一般来说,CmdletBinding 是将函数转换为高级函数的关键。在脚本顶部添加它可以使脚本成为“高级”脚本。函数和脚本大致相同,其中脚本文件名等同于函数名,而脚本内容则相当于函数中的脚本块部分。

CmdletBinding 属性可控制函数的功能,例如添加支持确认和 WhatIf(通过 SupportsShouldProcess),禁用参数的位置绑定等。


1
“其中脚本文件名称等同于函数名称,而脚本内容等同于函数的脚本块部分”。这是什么意思?如果我有一个单一的文件,里面充满了带有每个参数部分前的[CmdletBinding()]Function,那么它与脚本文件名有什么关系吗?它有关系吗? - ruffin
@ruffin "这与脚本文件名有关吗?" 不是的。Shay的比喻只是为了说明两个示例具有相同的功能。您文件中定义的函数名称以及这些函数包含[CmdletBinding()]与它们所包含的脚本文件名没有关系。 - Bren0man

22

CmdletBindingParameter等是特殊的属性类,脚本编写者可以使用它们来定义PowerShell的行为,例如使函数成为具有Cmdlet功能的高级函数。

当您通过例如[CmdletBinding ()]调用它们时,您将初始化该类的新实例。

MSDN上阅读有关CmdletBindingAttribute类的更多信息。

MSDN上阅读有关ParameterAttribute类的更多信息。

了解有关属性类的更多信息,请单击此处此处


1
好的。因此,在使用完整语法时,我们将传递一个哈希表给构造函数,该哈希表将用于设置公共成员的值? - Loïc MICHEL
1
以某种方式,哈希表由 ; 分隔。我认为这是一种特殊的类和构造方式,PowerShell 进程可以使用它。你可以把它想象成 c# 中的 new Question { Text = "Some question" };。如果你想知道它是如何工作的,可以尝试反编译我在 MSDN 链接中指定的库,并查看它是否可读。 - Frode F.
是的,有点像哈希表 :)。 - Loïc MICHEL
添加了一些关于属性类的链接,介绍了如何创建和使用它。这些特殊的类继承自 System.Attribute 或其他类。我只是把脚本编程作为一种爱好学习,所以并不是专家 :) - Frode F.
1
@{}是类型为System.Collections.Hashtable的对象,它实现了System.Collections.IDictionary接口。Hashtable自.NET 1.1版本以来一直存在。在C#中的等效代码如下:`new Hashtable { { "key1", "value1" }, { "key2", "value2" }, }` - Zenexer

4
关于语法问题,格式与使用C#中的命名参数将.NET属性类应用于成员非常相似。
比较来自PowerShell语言规范B.2.4节和C#语言规范C.2.13节的属性简化语法:

B.2.4 属性 (PowerShell)

attribute:
  [ attribute-name ( attribute-arguments ) ]

attribute-arguments:
  attribute-argument
  attribute-argument
, attribute-arguments

attribute-argument:
  simple-name
= expression


C.2.13 属性 (C#)

attribute:
  [ attribute-name ( named-argument-list ) ]

named-argument-list:
  named-argument
  named-argument-list
, named-argument

named-argument:
  identifier
= attribute-argument-expression

我认为,可能会通过使用哈希表初始化语法来初始化属性,例如[A(P=v)][A('P'=v)]$n = 'P'; [A($n=v)]等等,或一些特定的子集,并将分号作为分隔符字符,从概念上讲更简洁明了。但是,我相信支持所有选项(或其中的某个特定子集)会比它值得的麻烦多得多。
另一方面,如果您想要使用高级功能,则学习高级语法可能是有意义的 :)

属性不支持哈希表语法,因为它们不处理普通的键和值。这些键对应于扩展System.Attribute类中明确定义的属性。 - Zenexer
@Zenexer:这是支持类似于C#语法的一个论点,但这并不限制语言。同样地,我可以定义一个hashtable$h = @ { foo = 2 }并通过$h.foo属性语法访问元素,即使hashtable实际上没有“foo”属性。在一个案例中,语言设计者选择了一种灵活的语法,而在另一个案例中,他们选择支持简单的语法。 - Emperor XLII
当然,这涉及到一定的语言设计,但请记住PowerShell是建立在.NET之上的。 PowerShell属性是.NET属性,因此它们必须遵循.NET属性规则。扩展.NET属性并提供特定于PowerShell的版本肯定是可能的,但由于PowerShell的本质,必须有一种定义.NET属性的方式。 - Zenexer
我认为我们在谈论两件不同的事情 :) 我的回答集中在语法上:即语言用于实例化属性对象的文本形式是什么(在这种情况下,name + '=' + value),而您似乎关注的是属性类型本身的特性。我同意支持哈希表或任意PS对象而不是简单属性是不必要的,并且无论您是否可以写成"name"=value而不是name=value,这都不会改变name必须引用可分配属性的事实。 - Emperor XLII

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接