管道与参数参数输入?

Pipeline vs Parameter Argument input?

我对通过管道或参数输入接受值时的区别有何疑问。

我问是因为:

我这么说是因为,它确实从同一个变量中获取我的输入,直到它无法连接到一个变量,然后它停止了脚本。这有什么原因吗?

这是我的脚本,供所有好奇的人使用:

Function Set-DWRCVersion {
<#
NAME
    Set-DWRCVersion
    
SYNTAX
    Set-DWRCVersion [[-ComputerName] <string[]>]  [<CommonParameters>]

EXAMPLES
    Set-DWRCVersion -ComputerName LocalHost,ComputerOne
    'LocalHost','ComputerOne' | Set-DWRCVersion
    $CritNames | Set-DWRCVersion
    
    $CritNames | Set-DWRCVersion |Export-Csv "$($env:USERPROFILE.Substring(0,20))\Desktop\NewCrit.CSV" -Force -NoTypeInformation 
#>

[cmdletbinding()]
    Param(
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   ValueFromPipeLineByPropertyName=$true)]
                   [ValidateLength(1,15)]
                   [Alias('Comp','CN','name')]
                   [string[]]$ComputerName)
                   
Process{
        try{
        [string]$Path = "C:\Users\Abraham\Desktop\dwrcs"  #Dameware File Path (current)
            foreach($Computer in $ComputerName){
                $PSSession = New-PSSession -ComputerName $Computer -ErrorAction Stop
                $TestPath  = Invoke-Command -Session $PSSession -ScriptBlock { Test-Path -Path "C:\Windows\dwrcs" }
                    if($TestPath -eq $false){
                        Copy-Item -Path $Path -Destination "\$Computer\C$\Windows" -Recurse -Force }
                        #Start-Sleep -Seconds 1

                $EXEVersion = Invoke-Command -Session $PSSession -ScriptBlock { [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Windows\dwrcs\DWRCS.EXE").FileVersion }
                $DLLVersion = Invoke-Command -Session $PSSession -ScriptBlock { [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Windows\dwrcs\DWRCRSS.dll").FileVersion }
                    if($EXEVersion -notmatch '12.1.2.584'){
                        [PSCustomObject] @{
                                "Computer Name"  = $Computer
                                "Status"         = "Online"
                                "DWRC Version"   = $EXEVersion
                                "DWRCRSS DLL"    = $DLLVersion
                                "DWRC Check"     = "Not up to date" }
                                ""
                        Write-Output -InputObject "Version not current"
                        Write-Output -InputObject "Stopping Service. . ."
                        Invoke-Command -Session $PSSession -ScriptBlock { 
                            Stop-Service -Name dwmrcs;
                            Get-Item -Path C:\windows\dwrcs | Rename-Item -NewName "dwrcs.old" }
                            Remove-Item -Path "\$Computer\c$\Windows\dwrcs.old" -Recurse -Force -ErrorAction SilentlyContinue
                            #Start-Sleep 1

                        Write-Output -InputObject "Copying new files over. . ."
                        Copy-Item -Path $Path -Destination "\$Computer\C$\Windows" -Recurse -Force

                        Write-Output -InputObject "Starting Service. . ."
                        Invoke-Command -Session $PSSession -ScriptBlock { Start-Service -Name dwmrcs }
                              } 
                    elseif($EXEVersion -match '12.1.2.584') {
                        [PSCustomObject] @{
                                "Computer Name"  = $Computer
                                "Status"         = "Online"
                                "DWRC Version"   = $EXEVersion
                                "DWRCRSS DLL"    = $DLLVersion
                                "Version Check"  = "Up to Date" }
                              }
                    else { Write-Output -InputObject "Error Occured"
                           Throw "$($Error[0].Exception.Message)"  }
                    }
            } Catch {
                        [PSCustomObject] @{
                                "Computer Name"  = $Computer
                                "Status"         = "Offline"
                                "DWRC Version"   = $null
                                "DWRCRSS DLL"    = $null
                                "Version Check"  = $null }
            } Finally {
                if ($PSSession) {
                Get-PSSession | Remove-PSSession }
        }
    }
}

总的来说,这是一个非常简单的脚本,它将为我获取一些远程文件版本。我只是不确定为什么从管道接受值不会停止脚本,但使用常规参数输入它们会。

我正在从 csv 导入以使用以下方法获取名称:

$Crit = Import-Csv -Path "$($env:USERPROFILE.Substring(0,20))\Desktop\med.csv"
$CritNames = $Crit.'NetBios Name' -replace "AREA51\","" 

<# Names return like so:
COmputerOne
COmputerTwo
COmputerThr
Etc.. #>

当 运行 它使用管道输入时:$CritNames | Set-DWRCVersion 有效,但 Set-DWRCVersion -ComputerName $CritNames 无效;好吧,它会一直运行到离线计算机然后停止脚本。

我是不是漏了什么?比我聪明得多的人可以教育我吗?(:

您可以通过将 try/catch/finally 语句 移到 循环 foreach 中来解决此问题:

process {
  foreach($Computer in $ComputerName){
    try {
      # ...
    }
    catch {
      # ...
    }
    finally {
      # ...
    }
  }
}

为什么这会有所不同?

当通过 -ComputerName 显式绑定一个值时,process 块仅被调用 一次 - 但是当您通过管道提供输入时,process 块被调用 每个输入项一次 .

您可以使用如下简单的测试函数观察此行为:

function Test-ProcessInvocation {
  param(
    [Parameter(Mandatory,ValueFromPipeline)]
    [string[]]$ComputerName
  )

  process {
    "Running process block with $($ComputerName.Length) input arguments: [$ComputerName]"
  }
}

运行 这两种输入模式将使此行为清晰可见:

PS ~> Test-ProcessInvocation -ComputerName 1,2,3
Running process block with 3 input arguments: [1 2 3]
PS ~> 1,2,3 |Test-ProcessInvocation
Running process block with 1 input arguments: [1]
Running process block with 1 input arguments: [2]
Running process block with 1 input arguments: [3]

这意味着 运行 Set-DWRCVersion -ComputerName Computer1,Computer2 转换为以下语句序列:

# Single invocation of `process` block with $ComputerName = all input values at once
try {
  foreach($Computer in 'Computer1','Computer2'){

  }
}
catch {}
finally {}

运行 'Computer1','Computer2' |Set-DWRCVersion另一方面:

# `process` block repeats per input item
try {
  foreach($Computer in 'Computer1'){

  }
}
catch {}
finally {}

try {
  foreach($Computer in 'Computer2'){

  }
}
catch {}
finally {}

因此,在通过管道调用时在 foreach 循环内抛出错误永远不会“跳过”任何项目,因为循环一次只对一个项目进行操作。

通过反转循环和 try/catch 语句之间的关系,现在任何错误都将被捕获并在 内部 循环中处理,它将不再跳过 $ComputerName.

中剩余的输入值