管道与参数参数输入?
Pipeline vs Parameter Argument input?
我对通过管道或参数输入接受值时的区别有何疑问。
我问是因为:
- 在我的脚本中通过管道接受值,有效。
- 使用参数参数接受值,例如“
-ComputerName
”之类的作品。
我这么说是因为,它确实从同一个变量中获取我的输入,直到它无法连接到一个变量,然后它停止了脚本。这有什么原因吗?
这是我的脚本,供所有好奇的人使用:
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
.
中剩余的输入值
我对通过管道或参数输入接受值时的区别有何疑问。
我问是因为:
- 在我的脚本中通过管道接受值,有效。
- 使用参数参数接受值,例如“
-ComputerName
”之类的作品。
我这么说是因为,它确实从同一个变量中获取我的输入,直到它无法连接到一个变量,然后它停止了脚本。这有什么原因吗?
这是我的脚本,供所有好奇的人使用:
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
.