首先,我非常抱歉,冗长冗长的post。 这是一个有趣的问题,我想尽可能详细。 我试着通过网站上的任何相关的PowerShell职位,但我找不到任何帮助我解决这个问题。
我一直在与PowerShell脚本一起工作,可以将Wake-On-Lan数据包发送到一组计算机上。 它通过读取具有两列主机名和MAC的.csv文件来工作,然后为每台计算机创buildWOL数据包并在networking上广播它们。 WOL数据包发送后,等待一分钟,然后ping电脑,以确认他们在线,如果有任何不响应将显示一个窗口,哪些机器没有响应一个ping。 直到最后的If / Else语句正常工作,所以我不会在脚本的那部分上做太多的细节(当然,如果你想要/需要更多的细节,请随时询问)。
我遇到的问题是最后的If / Else声明。 脚本应该工作的方式是在脚本中间的ForEach循环中,根据计算机是否响应ping,variables$ PingResult的值是true或false。 如果ping失败,则$ PingResult为$ false,然后将主机名添加到$ PingResult2variables。
理论上,如果所有的机器都响应,那么If语句会触发,消息框会显示它是成功的,然后脚本停止。 如果有任何机器没有响应,则运行Else语句,它将$ PingResult2variables中的所有项目连接在一起,并在窗口中显示列表。 实际情况是,即使所有机器都响应ping,If语句也会被完全跳过,而Else语句会运行。 但是,在这一点上,$ PingResult2variables是空的,因此它不显示任何计算机名称,无法响应的机器。 在我的testing中,我从来没有见过脚本无法唤醒计算机的情况(假设它被插入等),但是Else语句仍然运行。 在运行Else语句的情况下,我已经检查了$ PingResult2variables的值,并确认它是空的,input$ PingResult2 -eq“”返回$ true。
要添加另一个皱纹的问题,我想返回到$ PingResult2variables。 我必须创buildvariables作为通用列表,以便它将支持Add方法,以允许variables根据需要增长。 作为一个testing,我们通过使用+ =操作符来修改脚本来将结果连接在一起,而不是使$ PingResult2成为一个列表,而在机器出现故障的情况下,在最终的显示窗口中没有给出可读的可视结果。实际上偶尔工作。 如果所有计算机成功响应,If语句将按预期方式运行并显示成功消息。 就像我说的那样,有时候会有效果,有时候不会,其他的改变都不会对结果产生影响。 我们尝试的另外一件事是将所有对Visual Basic程序集和其他GUI元素(除了Out-GridView窗口)的引用都删除,这也不起作用。
任何想法是什么可能导致这个问题? 我和我的团队在这一点上完全没有想法,我们很想知道是什么原因造成了这个问题。 我们已经在Windows 7,8.1以及Windows 10的最新预览版上尝试过了,但没有成功。 预先感谢您的帮助。
PS额外的布朗尼点,如果你可以解释什么29线正则expression式被称为以及如何确切的工作。 我在一个网页上发现了这个问题,解决了在每两个字符之间添加冒号的问题,但是这个post并没有解释它被称为什么。 (原文链接http://powershell.org/wp/forums/topic/add-colon-between-every-2-characters/ )
原WOL脚本,我们build立的脚本的其余部分是由约翰·萨维尔(链接http://windowsitpro.com/networking/q-how-can-i-easily-send-magic-packet-wake-machine-my-subnet )
脚本
Add-Type -AssemblyName Microsoft.VisualBasic,System.Windows.Forms $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.ShowDialog() | Out-Null $FileVerify = Get-Content -Path $OpenFileDialog.FileName -TotalCount 1 $FileVerify = ($FileVerify -split ',') If($FileVerify[0] -ne "Machine Name" -or $FileVerify[1] -ne "MAC") { $MsgBox = [System.Windows.Forms.MessageBox]::Show("The CSV File's headers must be Machine Name and MAC.",'Invalid CSV File headers!',0,48) Break } $ComputerList = Import-Csv -Path $OpenFileDialog.FileName | Out-GridView -PassThru -Title "Select Computers to Wake up" ForEach($Computer in $ComputerList) { If($Computer.'MAC' -notmatch '([:]|[-])') { $Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:' } $MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) } $UDPclient = new-Object System.Net.Sockets.UdpClient $UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000) $packet = [byte[]](,0xFF * 6) $packet += $MACAddr * 16 [void] $UDPclient.Send($packet, $packet.Length) write "Wake-On-Lan magic packet sent to $($Computer.'Machine Name'.ToUpper())" } Write-Host "Pausing for sixty seconds before verifying connectivity." Start-Sleep -Seconds 60 $PingResult2 = New-Object System.Collections.Generic.List[System.String] ForEach($Computer in $ComputerList) { Write-Host "Pinging $($Computer.'Machine Name')" $PingResult = Test-Connection -ComputerName $Computer.'Machine Name' -Quiet If ($PingResult -eq $false) { $PingResult2.Add($Computer.'Machine Name') } } If($PingResult2 -eq "") { [System.Windows.Forms.MessageBox]::Show("All machines selected are online.",'Success',0,48) Break } Else { $PingResult2 = ($PingResult2 -join ', ') [System.Windows.Forms.MessageBox]::Show("The following machines did not respond to a ping: $PingResult2",'Unreachable Machines',0,48) }
If
语句中的比较不正确,因为您将$PingResult2
( List<string>
)与字符串进行比较。 相反,尝试
If ($PingResult2.Count -eq 0) { # Show the message box } Else { # Show the other message box }
或在这个主题上的无数其他变化之一。
正在讨论的正则表达式使用反向引用来将相同的两个字符和一个冒号字符完全替换为两个字符。 不过,我不确定你究竟想要“定义”什么。
您正在检查列表是否具有空字符串的值,而不是检查列表中的项目数量。
如果将if语句更改为以下语句,它应该可以正常工作:
If($PingResult2.count -eq 0)
我猜正则表达式试图在字符串的每两个字符之间插入一个冒号来表示0123456789ab
为01:23:45:67:89:ab
。
代码表示MAC中是否有连字符或冒号,每个字符都用冒号分隔,然后用冒号作为分隔符分隔地址,然后分别表示为一个字节:
If($Computer.'MAC' -notmatch '([:]|[-])') { $Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:' } $MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) }
另一个答案很好地解释了为什么你的代码不工作。 我不去那里 相反,我会提出一些我认为会改善脚本的建议,并解释为什么我这么认为。 让我们从功能开始。 你做的一些事情是我手头上的功能,因为它们工作得很好,而且经常使用,所以我喜欢让它们得心应手。
首先,您的对话框获取CSV文件路径。 它的工作原理,不要误解我的意思,但可能会更好。因为它弹出一个没有参数的打开文件对话框。 这个函数允许你使用一些不同的参数,或者一个非常通用的打开文件对话框,但是我认为这是一个小的改进:
Function Get-FilePath{ [CmdletBinding()] Param( [String]$Filter = "|*.*", [String]$InitialDirectory = "C:\") [void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.initialDirectory = $InitialDirectory $OpenFileDialog.filter = $Filter [void]$OpenFileDialog.ShowDialog() $OpenFileDialog.filename }
然后就这样调用它:
$CSVFile = Get-FilePath -Filter "Comma Separated Value (.CSV)|*.CSV" -InitialDirectory "$env:USERPROFILE\Desktop"
这将打开只有CSV文件的对话框过滤,并启动他们看着他们的桌面(我发现很多人把东西保存到他们的桌面)。 这只是得到了路径,所以你会像你一样运行你的验证。 其实,不像你那样。 你真的好像有点复杂了。 我会马上得到,首先,另一个功能! 你经常调用消息框,并输出一堆选项,每次调用类型和所有东西。 如果你要不止一次地做这件事,让自己轻松一下,做一个功能。 在这里检查一下:
Function Show-MsgBox ($Text,$Title="",[Windows.Forms.MessageBoxButtons]$Button = "OK",[Windows.Forms.MessageBoxIcon]$Icon="Information"){ [Windows.Forms.MessageBox]::Show("$Text", "$Title", [Windows.Forms.MessageBoxButtons]::$Button, $Icon) | ?{(!($_ -eq "OK"))} }
然后,您可以指定尽可能多或尽可能少的。 加上它使用Type'd参数,所以选项卡完成工作,或在ISE(如果这是你写的脚本,就像我这样),它会弹出有效的选项,你只需从列表中选择按钮或图标以显示。 另外,如果它是一个简单的“OK”响应,它不会返回任何东西,保持干净,但会返回Yes / No / Cancel或您为按钮选择的其他选项。
好的,那就是功能,让我们来看看剧本。 你的文件验证…好吧,你拉的文件的第一行,所以应该只是一个字符串,我不知道为什么你分裂它,并单独验证每个头。 只是匹配整个字符串。 我建议这样做不区分大小写,因为我们并不在乎这里的情况。 另外,根据CSV文件的生成方式,可能会在头文件中引用引号,您可能需要考虑这些引号。 使用-Match
将执行一个更加宽容的RegEx匹配。
If((Get-Content $CSVFile -TotalCount 1) -match '^"?machine name"?,"?mac"?$'){ Show-MsgBox "The CSV File's headers must be Machine Name and MAC." 'Invalid CSV File headers!' -Icon Warning break }
所以现在我们有两个函数和五行代码。 是的,这些功能占用了比以前更多的空间,但是它们更友好,而且更实用。 就我而言,你的MAC地址改正,WOL发送部分都是王牌。 没有理由改变那部分。 现在,为了验证电脑已经恢复正常了…在这里我们可以使用一些改进。 而不是让[List]
只是添加一个成员到每个对象,然后过滤下面。 整个脚本会稍微长一些,但我觉得更好。
Add-Type -AssemblyName Microsoft.VisualBasic,System.Windows.Forms Function Get-FilePath{ [CmdletBinding()] Param( [String]$Filter = "|*.*", [String]$InitialDirectory = "C:\") [void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.initialDirectory = $InitialDirectory $OpenFileDialog.filter = $Filter [void]$OpenFileDialog.ShowDialog() $OpenFileDialog.filename } Function Show-MsgBox ($Text,$Title="",[Windows.Forms.MessageBoxButtons]$Button = "OK",[Windows.Forms.MessageBoxIcon]$Icon="Information"){ [Windows.Forms.MessageBox]::Show("$Text", "$Title", [Windows.Forms.MessageBoxButtons]::$Button, $Icon) | ?{(!($_ -eq "OK"))} } #Get File Path $CSVFile = Get-FilePath -Filter "Comma Separated Value (.CSV)|*.CSV" -InitialDirectory "$env:USERPROFILE\Desktop" #Validate Header If((Get-Content $CSVFile -TotalCount 1) -match '^"?machine name"?,"?mac"?$'){ Show-MsgBox "The CSV File's headers must be Machine Name and MAC." 'Invalid CSV File headers!' -Icon Warning break } $ComputerList = Import-Csv -Path $CSVFile | Out-GridView -PassThru -Title "Select Computers to Wake up" ForEach($Computer in $ComputerList) { If($Computer.'MAC' -notmatch '([:]|[-])') { $Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:' } $MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) } $UDPclient = new-Object System.Net.Sockets.UdpClient $UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000) $packet = [byte[]](,0xFF * 6) $packet += $MACAddr * 16 [void] $UDPclient.Send($packet, $packet.Length) write "Wake-On-Lan magic packet sent to $($Computer.'Machine Name'.ToUpper())" } Write-Host "Pausing for sixty seconds before verifying connectivity." Start-Sleep -Seconds 60 $ComputerList|ForEach { Write-Host "Pinging $($_.'Machine Name')" Add-Member -InputObject $_ -NotePropertyName "PingResult" -NotePropertyValue (Test-Connection -ComputerName $Computer.'Machine Name' -Quiet) } If(($ComputerList|Where{!($_.PingResult)}).Count -gt 0) { Show-MsgBox "All machines selected are online." 'Success' } Else { Show-MsgBox "The following machines did not respond to a ping: $(($ComputerList|?{!($_.PingResult)}) -join ", ")" 'Unreachable Machines' -Icon Asterisk }
好吧,我要脱掉肥皂盒回家,我的换班已经结束了,现在是换个冷的时间了。