4

I'm looking at the documentation of PowerShell's Rename-Item cmdlet and there is an example like this.

Get-ChildItem *.txt | Rename-Item -NewName { $_.name -Replace '\.txt','.log' }

This example shows how to use the Replace operator to rename multiple files, even though the NewName parameter does not accept wildcard characters.

This command renames all of the .txt files in the current directory to .log.

The command uses the Get-ChildItem cmdlet to get all of the files in the current folder that have a .txt file name extension. Then, it uses the pipeline operator (|) to send those files to Rename-Item .

The value of NewName is a script block that runs before the value is submitted to the NewName parameter.

Note the last sentence:

The value of NewName is a script block that runs before the value is submitted to the NewName parameter.

Actually NewName is a string:

[-NewName] <String>

So does that means I can always use a script block when the required parameter type is a string?

4

3 回答 3

9
# Delay-bind script-block argument:
# The code inside { ... } is executed for each input object ($_) and
# the output is passed to the -NewName parameter.
... | Rename-Item -NewName { $_.Name -replace '\.txt$','.log' }

上面的调用显示了延迟绑定脚本块{ ... })参数的应用程序,这是一个隐含的功能

  • 适用于设计为采用管道输入的参数,

    • 以下之外的任何类型,在这种情况下会发生常规参数绑定[1]

      • [scriptblock]
      • [object] ([psobject]但是,确实有效,因此也是等效的[pscustomobject])
      • (未指定类型),实际上与[object]
    • 这些参数是否接受通过( ValueFromPipeline) 或属性名称( ValueFromPipelineByPropertyName) 输入的管道是无关紧要的。

    • 有关如何发现给定 cmdlet 的管道绑定参数的信息,请参阅此答案;在最简单的情况下,例如:

      Get-Help Rename-Item -Parameter * | Where pipelineInput -like True*
      
  • 通过传递的脚本块而不是类型适当的参数启用 每个输入对象的转换;脚本块针对每个管道对象进行评估,可以$_像往常一样在脚本块内访问该脚本块,并且脚本块的输出- 假定与参数类型适合 - 用作参数。

    • 由于根据定义,此类临时脚本块与您要定位的参数类型不匹配,因此在传递它们时必须始终明确使用参数名称。

    • 延迟绑定脚本块无条件地提供对管道输入对象的访问,即使参数通常不会被给定的管道对象绑定,如果它被定义为ValueFromPipelineByPropertyName并且对象缺少该名称的属性。

    • 这启用了诸如以下调用之类的技术Rename-Item,其中来自的管道输入Get-Item- 像往常一样 - 绑定到-LiteralPath参数,但是将脚本块传递给-NewName- 通常只会绑定到具有.NewName属性的输入对象 - 可以访问相同的管道对象,从而从输入文件名派生目标文件名:

      • Get-Item file | Rename-Item -NewName { $_.Name + '1' } # renames 'file' to 'file1'; 输入绑定隐式)和脚本 块。-LiteralPath-NewName
    • 注意:与传递给ForEach-Objector的脚本块不同Where-Object,例如,延迟绑定脚本块在变量范围[2]中运行,这意味着您不能直接修改调用者的变量,例如跨输入对象递增计数器。
      作为一种解决方法,用于Get-Variable访问调用者的变量并访问其.Value在脚本块内的属性 - 请参阅此答案以获取示例。


[1] 错误情况:

  • 如果您错误地尝试将脚本块传递给不是管道绑定或者是[scriptblock]- 或 -[object]类型(无类型)的参数,则会发生常规参数绑定

    • 脚本块在管道输入处理开始之前传递一次,如果有的话。
      也就是说,脚本块作为(可能转换的)value传递,并且不会发生评估
      • 对于可转换为脚本块的类型[object][scriptblock]/委托类型的参数,脚本块将按原样绑定。System.Func
      • 在 (non-pipeline-binding)[string]类型参数的情况下,脚本块的文字内容作为字符串值传递。
      • 对于所有其他类型,参数绑定 - 以及整个命令 - 将简单地失败,因为从脚本块转换是不可能的。
  • 如果您在将延迟绑定脚本块传递给支持它们的管道绑定参数时忽略提供管道输入,您将收到以下错误

    • Cannot evaluate parameter '<name>' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.

[2] GitHub 问题 #7157中正在讨论这种差异。

于 2018-10-14T22:40:29.530 回答
4

那么这是否意味着当所需的参数类型是字符串时,我总是可以使用脚本块?:没有

这里的技术称为延迟绑定,在这种情况下非常有用。

当你延迟绑定时会发生什么?

PowerShell ParameteBinder 将了解延迟绑定的用法,并首先执行 ScriptBlock,然后将输出转换为相应参数的预期类型,这里是字符串。

下面是一个例子。

#Working one
'Path'|Join-Path -Path {$_} -ChildPath 'File'  

#Not working one
Join-Path -Path {'path'} -ChildPath 'File'
Join-Path : Cannot evaluate parameter 'Path' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.

要了解有关 ParameterBinding 的更多信息,您可以执行Trace-Command以下操作。

Trace-Command ParameterBinding -Expression {'Path'|Join-Path -Path {$_} -ChildPath 'File'} -PSHost
于 2018-10-14T19:21:05.323 回答
1

使用延迟绑定,参数可以使用脚本块而不是参数的实际数据类型从管道接收值。

在脚本块中,$_ 代表管道值。

它仅在管道上有输入时可用。

于 2018-10-14T20:41:27.103 回答