Powershell错误处理与函数

时间:2018-03-14 18:15:22

标签: powershell

在Powershell中寻找有关错误处理的建议。我想我理解使用Try/Catch背后的概念,但我正在努力在我的脚本中使用它或我需要多么精细。

例如,我应该在我的函数中使用try/catch吗?如果是,我应该在try中插入我的函数的动作还是我需要打破它

进一步下降?或者,我应该在调用函数时尝试处理错误吗?做这样的事情:

Try{
     Get-MyFunction 
    } catch{ Do Something" 
} 

以下是我编写的脚本示例,该脚本正在检查设备上的某些危害指示。我有一个应用程序将启动此脚本并捕获最终输出。应用程序要求最终输出采用以下格式,因此任何失败都应生成此输出。

[output]
result=<0 or 1>
msg= <string>

我正在这样做:

Write-Host "[output]"
Write-Host "result=0"
Write-Host "msg = $VariableContainingOutput -NoNewline

我的两个函数创建自定义对象,然后将它们组合成最终输出,因此我想以相同的格式捕获任何错误。如果一个函数生成错误,它应该记录这些并继续。

如果我只是自己运行代码(不使用函数),这可以工作,但是使用该函数我的错误不会被捕获。

这需要适用于PowerShell 2及更高版本。此脚本调用的Add-RegMember和Get-RegValue函数不会显示。

function Get-ChangedRunKey {
    [CmdletBinding()]
    param()
    process 
    {
        $days = '-365' 
        $Run = @()
        $AutoRunOutput = @()
        $RunKeyValues = @("HKLM:\Software\Microsoft\Windows\CurrentVersion\Run", 
                      "HKLM:\Software\Wow6432node\Microsoft\Windows\CurrentVersion\Run", 
                      "HKU:\S-1-5-21-*\Software\Microsoft\Windows\CurrentVersion\Run",
                      "HKU:\S-1-5-21-*\Software\Wow6432node\Microsoft\Windows\CurrentVersion\Run"
                      )
        Try{
            $Run += $RunKeyValues | 
            ForEach-Object {
                Get-Item $_ -ErrorAction SilentlyContinue | 
                Add-RegKeyMember -ErrorAction SilentlyContinue | 
                Where-Object {
                    $_.lastwritetime -gt (Get-Date).AddDays($days)
                } | 
                Select-Object Name,LastWriteTime,property
            } 
            if ($Run -ne $Null)
            {
                $AutoRunPath = ( $Run | 
                    ForEach-Object {
                        $_.name
                    }
                ) -replace "HKEY_LOCAL_MACHINE", "HKLM:" -replace "HKEY_Users", "HKU:"
                $AutoRunValue = $AutoRunPath  | 
                    Where-Object { 
                        $_ -and $_.Trim() 
                    } |
                    ForEach-Object {
                        Get-RegValue -path $_ -Name '*' -ErrorAction SilentlyContinue
                    }
            }
        #Build Custom Object if modified Run keys are found 
            if($AutorunValue -ne $null)
            {
                foreach ($Value in $AutoRunValue) {
                    $AutoRunOutput += New-Object PSObject -Property @{
                        Description = "Autorun"
                        path = $Value.path
                        value = $Value.value
                    }
                }
            }
            Write-Output $AutoRunOutput
        }catch{
                $AutoRunOutput += New-Object PSObject -Property @{
                    Description = "Autorun"
                    path = "N/A"
                    value = "Error accessing Autorun data. $($Error[0])"
                }
        }
    }
}
function Get-ShellIOC {
    [CmdletBinding()]
    param()
    process 
    {
        $ShellIOCOutput = @()
        $ShellIOCPath = 'HKU:\' + '*' + '_Classes\*\shell\open\command'
     Try{
            $ShellIOCValue = (Get-Item $ShellIOCPath -ErrorAction SilentlyContinue | 
                Select-Object name,property | 
                ForEach-Object {
                    $_.name
                }
            ) -replace "HKEY_LOCAL_MACHINE", "HKLM:" -replace "HKEY_Users", "HKU:" 
            $ShellIOCDetected = $ShellIOCValue | 
                ForEach-Object {
                    Get-RegValue -path $_ -Name '*' -ErrorAction SilentlyContinue
                } | 
                Where-Object {
                    $_.value -like "*cmd.exe*" -or 
                    $_.value -like "*mshta.exe*"
                }
            if($ShellIOCDetected -ne $null) 
            { 
                foreach ($ShellIOC in $ShellIOCDetected) {
                    $ShellIOCOutput += New-Object PSObject -Property @{
                        Description = "Shell_IOC_Detected"
                        path = $ShellIOC.path
                        value = $ShellIOC.value
                    }
                }
            }
            Write-Output $ShellIOCOutput
        }catch{
                $ShellIOCOutput += New-Object PSObject -Property @{
                    Description = "Shell_IOC_Detected"
                    path = "N/A"
                    value = "Error accessing ShellIOC data. $($Error[0])"
                    }
        }
    }
}
function Set-OutputFormat {
    [CmdletBinding()]
    param()
    process 
    {   
        $FormattedOutput = $AutoRunOutput + $ShellIOCOutput | 
        ForEach-Object {
            "Description:" + $_.description + ',' + "Path:" + $_.path + ',' + "Value:" + $_.value + "|"
        }
        Write-Output $FormattedOutput 
    }
}

if (!(Test-Path "HKU:\")){
    try{
        New-PSDrive -PSProvider Registry -Root HKEY_USERS -Name HKU -ErrorAction Stop | Out-Null
    }catch{
        Write-Output "[output]"
        Write-Output "result=0"
        Write-Host "msg = Unable to Connect HKU drive" -NoNewline
    }
}  
$AutoRunOutput = Get-ChangedRunKey

$ShellIOCOutput = Get-ShellIOC 

$FormattedOutput = Set-OutputFormat 

Write-Output "[output]"

if ($FormattedOutput -eq $Null)
{
    Write-Output "result=0"
    Write-Host "msg= No Items Detected" -NoNewline
}
else
{
    Write-Output "result=1"
    Write-Host "msg=Items Detected: $($FormattedOutput)" -NoNewline
}

1 个答案:

答案 0 :(得分:1)

您必须知道PowerShell中有两种错误类型:

  • 终止错误:那些会在catch块中自动捕获

  • 非终止错误:如果要捕获它们,则需要使用-ErrorAction Stop执行相关命令。如果它不是PowerShell命令而是可执行文件,那么您需要检查退出代码或$?之类的内容。因此,我建议您将整个操作包装在一个高级功能中,然后使用-ErrorAction Stop进行调用。

除此之外,我想说PowerShell版本2已被弃用。存在非终止错误的原因是因为存在例如处理来自管道的多个对象的情况,您可能不希望它停止仅因为它不适用于一个对象。请不要使用Write-Host,请根据使用情况使用Write-VerboseWrite-Output