我目前正在编写一个脚本,用于从域中的计算机收集大量信息。因为我想显示它已经完成了多少(任务可能运行数小时或数分钟),所以我想使用 Write-progress。工作正常,直到我开始通过管道传递计算机列表,而不是作为“正常”参数。
考虑以下代码:
Function Test-SomethingOnAllComputers {
[CmdletBinding()]
param (
[parameter(
valuefrompipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias("DNSHostName")
[string[]]$ComputerName
)
BEGIN {
## Do some init. stuff
Write-progress -PercentComplete 0 -Status "Init..."
$i = 1
}
PROCESS {
# Do something with each computername passed in.
Write-Progress -PercentComplete ($Input.Count/$i*100) -Status "Doing stuff to $ComputerName"
}
END {
Write-Progress -PercentComplete 100 -status "Done with all objects in the pipeline. Finalizing..."
# Clean up and complete the job..
Write-Progress -PercentComplete 100 - Status "Finished.." -Completed
}
}
用法: Get-ADComputer -Filter {Name -like "Prod*"} | Test-SomethingOnAllComputers
我这里有两个问题:
我获取的数据
Get-ADComputer
是 DNSHostName 而不是 ComputerName(因此是别名),但当我在部分中引用 ComputerName 时,它似乎没有被拾取PROCESS
。如果我将参数更改为 DNSHostName,我会得到正确的值。所以我猜我只是错过了与管道非常相关的一些内容。- 可以通过以下方式解决(是的,感谢微软,这看起来非常好......):
Get-ADComputer -Filter { Name -like "Prod*"} | % {[string[]]$_.DNSHostName}| Test-SomethingOnAllComputers
谢谢杰布史密斯为了解决这个问题
我无法获取通过管道传递的对象总数。有没有办法在不循环整个输入对象管道的情况下做到这一点?
答案1
- 这是 AD cmdlet 返回对象的一个已知问题。如果您向它们请求它们没有的属性,它们会自动将其创建为 $null。这种令人讨厌的非标准行为在我第一次将对象导入我自己的 PowerShell 函数时也让我感到困惑。因此,PowerShell 首先尝试绑定 ComputerName 属性,并获取 $null,因此它从不尝试别名。只需交换别名和实际参数名称,一切就会按预期工作。出于这个原因,我对所有 ComputerName 参数都执行了同样的操作。这不是典型的情况,您尝试执行的操作基本上可以与任何其他 cmdlet 一起使用。
我会让其他人来处理你的第二个问题——我不知道有什么好的方法可以做到这一点,同时又能轻松地保留进入你的功能的能力。
答案2
这似乎不是一个一般的管道错误,而是 AD Cmdlet 和从 Active Directory 加载的属性的填充方式的一个怪癖。
如果您将整个 Computer 对象作为Object
s 进行管道传输(在 param 块中使用 [Object[]] 进行转换),并使用 检查对象的属性$_.psobject.Properties
,您应该看到,在内部,传递给 Test- cmdlet 的对象具有存储在名为 的单个数组属性中的所有 AD 属性Properties
(命名混乱),并且将对象通过管道传输到 Get-Member 时看到的“属性”实际上是动态的并从该数组中填充的。
确保DNSHostName
在管道化计算机对象时属性被填充并且正常工作的更优雅的方法是:
Get-ADComputer -Filter * | Select-Object -Property DNSHostName | Test-SomethingOnAllComputers
或者
Get-ADComputer -Filter * | Select-Object -Property * | Test-SomethingOnAllComputers
如果您愿意的话,这应该会强制属性持久化,就像您的 foreach 示例一样。
关于Write-Progress
,问题是,当从另一个 Cmdlet 进行管道传输时,您不可能知道在开始处理第一个块时会预期多少个对象。
如果您想要使用,Write-Progress -PercentCompleted
则需要先将所有计算机加载到一个集合中,然后运行 cmdlet Test-
,从而有效地挫败您的流水线工作:
$Computers = Get-ADComputer -Filter * |Select-Object -ExpandProperty DNSHostName
Test-SomethingOnAllComputers -ComputerName $Computers
然后在 Begin 块中添加如下内容:
$ComputerCount = $ComputerName.Count
$i = 0
现在你应该可以做到
$i++
Write-Progress -Activity "Doing Stuff" -Status "Doing stuff to $ComputerName" -PercentComplete $($i/$ComputerCount * 100)
不要对管道输入执行此操作,因为$ComputerCount
结果为零,而且只有查克·诺里斯 (Chuck Norris) 才能除以零