Windows PowerShell-If/Else语句不';我不能正常工作

Windows PowerShell-If/Else语句不';我不能正常工作,windows,powershell,if-statement,wake-on-lan,Windows,Powershell,If Statement,Wake On Lan,首先,我为这篇冗长的文章道歉。这是一个有趣的问题,我希望尽可能详细。我曾尝试在网站上查看任何有关PowerShell的帖子,但找不到任何有助于我解决此问题的内容 我一直在与一个团队一起编写PowerShell脚本,该团队可以向一组计算机发送Lan唤醒数据包。它的工作原理是读取一个.csv文件,该文件包含两列主机名和MAC,然后为每台计算机创建WOL数据包并在网络上广播。发送WOL数据包后,它会等待一分钟,然后ping计算机以验证它们是否联机,如果任何计算机没有响应,它将显示一个窗口,其中显示哪些

首先,我为这篇冗长的文章道歉。这是一个有趣的问题,我希望尽可能详细。我曾尝试在网站上查看任何有关PowerShell的帖子,但找不到任何有助于我解决此问题的内容

我一直在与一个团队一起编写PowerShell脚本,该团队可以向一组计算机发送Lan唤醒数据包。它的工作原理是读取一个.csv文件,该文件包含两列主机名和MAC,然后为每台计算机创建WOL数据包并在网络上广播。发送WOL数据包后,它会等待一分钟,然后ping计算机以验证它们是否联机,如果任何计算机没有响应,它将显示一个窗口,其中显示哪些计算机没有响应ping。直到最后的If/Else语句工作正常为止,所以我不会对脚本的这一部分进行太多的详细说明(当然,如果您想要/需要更多详细信息,请随时询问)

我遇到的问题是最终的If/Else语句。脚本应该工作的方式是,在脚本中间的Frach循环中,变量$Ping Read的值是真还是假取决于计算机是否响应ping。如果ping失败,$PingResult为$false,然后将主机名添加到$PingResult2变量中

理论上,如果所有机器都响应,if语句将激发,消息框将显示成功,然后脚本停止。如果任何计算机未能响应,则会运行Else语句,并将$PingResult2变量中的所有项连接在一起,并在窗口中显示列表。 实际上,即使所有机器都响应ping,if语句也会被完全跳过,而Else语句会运行。但是,此时$PingResult2变量为空,因此它不会显示未响应的计算机的任何计算机名称。在我的测试中,我从未见过脚本无法唤醒计算机的情况(假设它已插入,等等),但Else语句仍然运行。在Else语句运行的情况下,我检查了$PingResult2变量的值并确认它为空,然后键入$PingResult2–eq“”返回$true

为了给这个问题增加另一个麻烦,我想返回$PingResult2变量。我必须将变量创建为一个通用列表,以便它支持Add方法,允许变量根据需要增长。作为测试,我们修改了脚本,使用+=操作符将结果连接在一起,而不是将$PingResult2作为列表,虽然如果机器出现故障,在最终显示窗口中不会给出非常可读的可视结果,但它确实偶尔能正常工作。如果所有计算机都成功响应,If语句将按预期运行并显示成功消息。正如我所说的,它有时有效,有时无效,没有其他变化会对结果产生影响。我们尝试的另一件事是去掉对VisualBasic程序集和其他GUI元素的所有引用(除了OutGridView窗口),但这也不起作用

知道是什么导致了这个问题吗?我和我的团队在这一点上完全没有想法,我们很想找出问题的原因。我们已经在Windows7、8.1和Windows10的最新预览版上试用过,但没有成功。提前感谢您的帮助

如果你能解释第29行的正则表达式叫什么,以及它到底是如何工作的,那就有额外的布朗尼点了。我在一篇解决了在每两个字符之间添加冒号的问题的网络帖子中发现了这一点,但帖子没有解释它的名称。(原始链接)

我们构建的原始WOL脚本其余部分由John Savill(链接)编写

脚本

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
(一个
列表
)与字符串进行比较。相反,试试看

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"))}
}
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
}
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

    }