管道输入函数——写入进度和别名

管道输入函数——写入进度和别名

我目前正在编写一个脚本,用于从域中的计算机收集大量信息。因为我想显示它已经完成了多少(任务可能运行数小时或数分钟),所以我想使用 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

我这里有两个问题:

  1. 我获取的数据Get-ADComputer是 DNSHostName 而不是 ComputerName(因此是别名),但当我在部分中引用 ComputerName 时,它​​似乎没有被拾取PROCESS。如果我将参数更改为 DNSHostName,我会得到正确的值。所以我猜我只是错过了与管道非常相关的一些内容。

    • 可以通过以下方式解决(是的,感谢微软,这看起来非常好......):

    Get-ADComputer -Filter { Name -like "Prod*"} | % {[string[]]$_.DNSHostName}| Test-SomethingOnAllComputers

    谢谢杰布史密斯为了解决这个问题

  2. 我无法获取通过管道传递的对象总数。有没有办法在不循环整个输入对象管道的情况下做到这一点?

答案1

  1. 这是 AD cmdlet 返回对象的一个​​已知问题。如果您向它们请求它们没有的属性,它们会自动将其创建为 $null。这种令人讨厌的非标准行为在我第一次将对象导入我自己的 PowerShell 函数时也让我感到困惑。因此,PowerShell 首先尝试绑定 ComputerName 属性,并获取 $null,因此它从不尝试别名。只需交换别名和实际参数名称,一切就会按预期工作。出于这个原因,我对所有 ComputerName 参数都执行了同样的操作。这不是典型的情况,您尝试执行的操作基本上可以与任何其他 cmdlet 一起使用。

我会让其他人来处理你的第二个问题——我不知道有什么好的方法可以做到这一点,同时又能轻松地保留进入你的功能的能力。

答案2

这似乎不是一个一般的管道错误,而是 AD Cmdlet 和从 Active Directory 加载的属性的填充方式的一个怪癖。

如果您将整个 Computer 对象作为Objects 进行管道传输(在 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) 才能除以零

相关内容