我正在查看PowerShell的Rename-Item
cmdlet的文档,其中有一个类似的示例。
Get-ChildItem *.txt | Rename-Item -NewName { $_.name -Replace '.txt','.log' }
此示例显示如何使用Replace运算符重命名多个文件,即使NewName参数不接受通配符字符。
此命令将当前目录中的所有.txt文件重命名为.log
该命令使用Get-ChildItem cmdlet获取中的所有文件文件扩展名为.txt的当前文件夹。然后,它使用管道运算符(|)将这些文件发送到Rename Item。
NewName的值是在值提交给NewName参数。
注意最后一句话:
NewName的值是在该值提交给NewName参数之前运行的脚本块。
实际上NewName
是一个字符串:
[-NewName] <String>
那么,这是否意味着当所需的参数类型是字符串时,我总是可以使用脚本块?
# 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'
;input绑定到-LiteralPath
(隐式)和-NewName
脚本块
注意:与传递给
ForEach-Object
或Where-Object
的脚本块不同,例如,延迟绑定脚本块在子变量范围[2]中运行,这意味着您不能直接修改调用方的变元,例如在输入对象之间增加计数器
作为一种解决方法,使用Get-Variable
可以访问调用方的变量,并访问脚本块内的.Value
属性-有关示例,请参阅此答案。
[1]错误条件:
如果您错误地尝试将脚本块传递给不是管道绑定或是
[scriptblock]
-或[object]
-类型化(非类型化)的参数,则会发生常规参数绑定:- 在管道输入处理开始之前(如果有的话),脚本块被传递一次
也就是说,脚本块作为(可能转换的)值传递,并且不进行求值。- 对于类型为
[object]
或[scriptblock]
/可转换为脚本块的委托类型(如System.Func
)的参数,脚本块将按原样绑定 - 在(非管道绑定)
[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期中进行了讨论。
这意味着当需要参数类型是字符串?:否
这里的技术被称为延迟绑定,在这种情况下非常有用。
执行延迟绑定时会发生什么
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
使用延迟绑定,参数可以使用脚本块而不是参数的实际数据类型从管道接收值。
在脚本块中,$_代表管道值。
只有当管道上有输入时,它才可用。