solving sql server problems for millions of dbas and developers since 2006


Identify and resolve SQL Server problems BEFORE they happen with SQL diagnostic manager

SQL Server DBA Tips SQL Server Developer Tips SQL Server Business Intelligence Tips SQL Server Career Tips SQL Server Whitepapers SQL Server Tools SQL Server Webcasts SQL Server Questions and Answers SQL Server Questions and Answers


SQL Product Highlight

Idera - SQL compliance manager

SQL compliance manager is a comprehensive auditing solution that tells you who did what, when and how on your SQL Servers. SQL compliance manager helps you ensure compliance with regulatory and data security requirements.

Learn more!








Try Catch Finally with Powershell

By: | Read Comments | Print

Chad is an Architect, Administrator and Developer with technologies such as SQL Server, .NET, and Windows Server.

Related Tips: More

I ran across Adam Weigert's posting/script that provides a PoSh script that allows try/catch/finally like behavior - very cool. I started using the script myself and found that as much as I loved it, I wanted to add some additional 'features' to what it provides. I was looking for pipeline support (including the ability to throw 'soft' errors in the pipeline that would allow the pipeline to continue processing), some optional verbose debugging/error related information (particularly handy when using the script in a large distributed environment and you need information in your error logs about where the error came from, what system the script failed on, etc.), alias configuration, and further support for allowing friendly dot-sourcing of the script.

So, here's a version adapted from what Adam wrote about, links to his original posting are included in the script as well (see the get-usage function).

Enjoy!

Invoke-TryCatch.ps1

Chad Boyd ~~~ This posting is provided "AS IS" with no warranties, and confers no rights. Use of any included script samples are subject to the terms specified at http://www.mssqltips.com/disclaimer.asp and http://www.mssqltips.com/copyright.asp.

----------------------------------------- CODE BELOW -----------------------------------------

# See the Get-Usage function contents in the begin{} section for usage/comment details
param
(
    [ScriptBlock] $Try,
    [ScriptBlock] $Catch = { Throw $_ },
    [ScriptBlock] $Finally,
    [switch] $SoftErrorInPipeline
)
begin {
    function Get-Usage {
@"
    NAME
        Invoke-TryCatch
    SYNOPSIS
        Simulates Try,Catch,Finally handling for scripts
    SYNTAX
        Try {<try_block>} -Catch {<catch_block>} [-Finally {<finally_block>}] [-SoftErrorInPipeline]
    DETAILED DESCRIPTION
        Same functionality as try/catch/finally with .NET - specify the script blocks that make
        up the try/catch/finally handling and call into the function using the sytax specified
        above and below.
        Supports input from the pipeline, dot-sourcing, and invoking directly - if a value is not
        specified for the $Try block and we are in the pipeline, the input object will be either
        cast directly to the $Try block (if the input object is a ScriptBlock object) or will be
        invoked via Invoke-Expression (which supports string-type input to be tried).
        Adapted from Adam Weigert's blog entry here:
http://weblogs.asp.net/adweigert/archive/2007/10/10/powershell-try-catch-finally-comes-to-life.aspx
    PARAMETERS
        -command <ScriptBlock>
            Script block that makes up the Try section
            Required?            True
            Position?            1
            Default value        <required>
            Accept pipeline?    False
            Accept wildcards?    False
        -catch <ScriptBlock>
            Script block that makes up the Catch section
            Required?            True
            Position?            2
            Default value        <required>
            Accept pipeline?    False
            Accept wildcards?    False
        -finally <ScriptBlock>
            Script block that makes up the Finally section
            Required?            False
            Position?            3
            Default value        {}
            Accept pipeline?    False
            Accept wildcards?    False
        -SoftErrorInPipeline <switch>
            If set and being used in the pipeline, errors caught from the $try block in the
            catch will be raised via the write-error cmdlet vs. a direct Throw - this allows
            the pipeline to continue processing vs. being stopped as it will be if a Throw
            is used.
            Required?            False
            Position?            4
            Default value        False
            Accept pipeline?    False
            Accept wildcards?    False
    INPUT TYPE
        ScriptBlock,ScriptBlock,[ScriptBlock],[Switch]
    RETURN TYPE
        Dependent on the script blocks passed
    NOTES
        Alias created called "Try"
        Adapted from Adam Weigert's blog entry here:
http://weblogs.asp.net/adweigert/archive/2007/10/10/powershell-try-catch-finally-comes-to-life.aspx
        -------------------------- EXAMPLE 1 --------------------------
            Try {
                echo " ::Do some work..."
                echo " ::Try divide by zero: `$(0/0)"
            } -Catch {
                echo "  ::Cannot handle the error (will rethrow): `$_"
                #throw `$_
            } -Finally {
                echo " ::Cleanup resources..."
            }
        -------------------------- EXAMPLE 2 --------------------------
        The following example will pull all ps1 files from the current directory,
        pull the content of the file into the pipeline, and pass the content directly
        to the try block (default Catch block will simply throw any error).
            dir *.ps1 | get-content | try

"@
    }    # Get-Usage
    if (($MyInvocation.InvocationName -ne '.' -and $MyInvocation.InvocationName -ne '&') -and
        ($Args[0] -eq "-?" -or $Args[0] -eq "/?" -or $Args[0] -eq "-help" -or $Args[0] -eq "/help")) {
            $showUsage = $true;
            &Get-Usage;
            return;
    }
    if ($MyInvocation.InvocationName -ne '.') {    Write-Debug "Invoke-TryCatch::BEGIN"; }
    $InPipeline = $false;
    # Info for reporting error data...
    if ($VerbosePreference -ne "SilentlyContinue") {
        $myInfoScriptFullPath = $MyInvocation.ScriptName;
        $myInfoScriptName = $myInfoScriptFullPath.Substring($myInfoScriptFullPath.LastIndexOf("\") + 1);
        &{
            # we simply ignore errors initializing myInfo
            $local:ErrorActionPreference = "SilentlyContinue";
            $script:myInfo = @{
                "MachineName" = (get-content env:computername);
                "MachineDomain" = (get-content env:userdomain);
                "MachineDnsDomain" = (get-content env:userdnsdomain);
                "UserName" = (get-content env:username);
                "UserProfile" = (get-content env:userprofile);
                "ScriptName" = $myInfoScriptName;
                "ScriptFullPath" = $myInfoScriptFullPath;
                "ScriptPath" = $($myInfoScriptFullPath.Substring(0, $myInfoScriptFullPath.Length - $myInfoScriptName.Length));
            }
        }
    }    # if ($VerbosePreference -ne "SilentlyContinue")
    function Show-Exception {
        param (
            [System.Management.Automation.ErrorRecord] $errobj
        )
        # Output some verbose error information if requested...
        if ($VerbosePreference -ne "SilentlyContinue") {
            $local:ErrorActionPreference = "Continue"
            $myInfo.PipelineProcessing = $InPipeline;
            $msg = $myInfo | Sort-Object | Format-Table -autosize | Out-String;
            $msg = "`n `n-------VERBOSE EXCEPTION INFORMATION------- `n `n$msg`n `n-------------------------------------------`n ";
            # toss it up...
            Write-Error $msg;
        }

        # Rethrow the error, either continuing the pipeline or not...
        if (($SoftErrorInPipeline) -and ($InPipeline)) {
            # If we are in a pipeline and
            $local:ErrorActionPreference = "Continue"
            Write-Error $errobj;
        } else {
            #Throw the exception...
            Throw $errobj;
        }
    }
    function Invoke-TryCatch {
        param
        (
            [ScriptBlock] $Try,
            [ScriptBlock] $Catch = { Throw $_ },
            [ScriptBlock] $Finally,
            [switch] $SoftErrorInPipeline
        )
        begin {}    # Invoke-TryCatch::begin
        process {
            # Store meta-data depending on pipeline vs. invoked processing...
            if (($_) -or ($_ -eq 0)) {
                $InPipeline = $true;
                if ($VerbosePreference -ne "SilentlyContinue") {
                    $local:ErrorActionPreference = "SilentlyContinue";
                    $myInfo.PipelineObject = $_;
                    $myInfo.PipelineObjectType = $_.GetType();
                }
            }    # if($VerbosePreference -ne "SilentlyContinue")
            # If we don't have a $Try value, figure it out now
            if (-not $Try) {
                if ($_) {
                    # Try setting/casting the input object to a script block - if it works, we'll use the pipeline
                    # object as the $try, otherwise we'll throw a parameter exception...
                    trap {
                        Write-Debug "Invoke-TryCatch::Could not convert pipeline object to ScriptBlock [$_]."
                        continue;
                    }
                    # Get the script block...
                    if ("$($_.GetType())" -eq "System.Management.Automation.ScriptBlock") {
                        $Try = $_;
                    } else {
                        $Try = {Invoke-Expression $_};
                    }
                } else {
                    # No pipeline input - so, if we are invoked throw an error, otherwise the $Try will be nothing...
                    if ($MyInvocation.InvocationName -eq '&') {
                        # Invoked, throw an exception
                        Throw "The parameter -Try is required. [$($MyInvocation.InvocationName)] [$($MyInvocation.MyCommand.Name)] [$_]";
                    }               
                }
            }
            # If we have a value, show it if debugging, otherwise show a warning
            if ($Try) {
                Write-Debug "Invoke-TryCatch::Have `$Try value of [$Try] [$_]";
            } else {
                # Not invoked, show a warning - don't throw an error, as that will stop pipeline processing if we're in the pipe...
                Write-Warning "Invoke-TryCatch::No value could be determined for the `$Try block, will continue pipeline without processing this object"
            }
            # Process the try/catch/finally...
            & {
                # Explicitly set our preference for local use...
                $local:ErrorActionPreference = "SilentlyContinue";
                trap {
                    trap {
                        & {
                            if ($Finally) {
                                trap {
                                    Write-Debug "TryCatch::TRAP::Finally Block 2 Error [$_]";
                                    Show-Exception $_;
                                }
                                Write-Debug "TryCatch::Finally Block 2 Start";
                                &$Finally
                            }    # if ($Finally)
                        }
                        Write-Debug "TryCatch::TRAP::Catch Block Error [$_]";
                        Show-Exception $_;
                    }
                    Write-Debug "TryCatch::TRAP::Try Block Error [$_]";
                    if ($Catch) {
                        # Pipe along the current exception to the catch block to be processed
                        $_ | & {
                            Write-Debug "TryCatch::Catch Block Start";
                            # Store the original exception record...
                            if ($VerbosePreference -ne "SilentlyContinue") {
                                & {
                                    $local:ErrorActionPreference = "SilentlyContinue";
                                    $myInfo.Error = $_;
                                    $myInfo.ErrorInvocationName = $_.InvocationInfo.InvocationName;
                                    $myInfo.ErrorLine = $_.InvocationInfo.Line;
                                    $myInfo.ErrorCommand = $_.InvocationInfo.MyCommand;
                                    $myInfo.ErrorOffsetInLine = $_.InvocationInfo.OffsetInLine;
                                    $myInfo.ErrorPipeLineLength = $_.InvocationInfo.PipeLineLength;
                                    $myInfo.PipeLinePosition = $_.InvocationInfo.PipeLinePosition;
                                    $myInfo.ErrorPositionMessage = $_.InvocationInfo.ErrorPositionMessage;
                                    $myInfo.ErrorScriptLineNumber = $_.InvocationInfo.ScriptLineNumber;
                                    $myInfo.ErrorScriptName = $_.InvocationInfo.ScriptName;
                                    $myInfo.ErrorCategoryInfo = $_.CategoryInfo.GetMessage();
                                }
                            }    #    if ($VerbosePreference -ne "SilentlyContinue")
                            &$Catch
                        }
                    }    # if ($Catch)
                }
                if ($Try) {
                    Write-Debug "TryCatch::Try Block Start";
                    &$Try
                }    # if ($Try)
            }
            & {
                trap {
                    Write-Debug "TryCatch::TRAP::Finally Block 1 Error [$_]";
                    Show-Exception $_ ;
                }
                if ($Finally) {
                    Write-Debug "TryCatch::Finally Block 1 Start";
                    &$Finally
                }    # if ($Finally)
            }
        }    # Invoke-TryCatch::process
        end {}    # Invoke-TryCatch::end
    }
    if (-not (Get-Alias -Name try -ErrorAction SilentlyContinue)) {
        Set-Alias -Name try -Value Invoke-TryCatch -Description 'Simulates Try,Catch,Finally handling for scripts';
    }
}    # Invoke-TryCatch::begin
process {
    # No processing if we are dot-sourced or if we were just asked for a little help...
    if ($showUsage -or $MyInvocation.InvocationName -eq '.') {
        return;
    }

    # pass processing to the function via pipelining or invoke...
    if (($_) -or ($_ -eq 0)) {
        $InPipeline = $true;
        if ($SoftErrorInPipeline) {
            $_ | Invoke-TryCatch $Try $Catch $Finally -SoftErrorInPipeline;
        } else {
            $_ | Invoke-TryCatch $Try $Catch $Finally;
        }
    } else {    # if ($_)
        if ($SoftErrorInPipeline) {
            Invoke-TryCatch $Try $Catch $Finally -SoftErrorInPipeline;
        } else {
            Invoke-TryCatch $Try $Catch $Finally;
        }
    }    # if (($_) -or ($_ -eq 0))

}    # Invoke-TryCatch::process
end {
    if ((-not $showUsage) -and ($MyInvocation.InvocationName -ne '.')) { Write-Debug "Invoke-TryCatch::END"; }
}    # Invoke-TryCatch::end

 



Related Tips: More | Become a paid author


Last Update: 10/4/2008

Share: Share 






Comments and Feedback:


Post a Comment or Question

Keep it clean and stay on the subject or we may delete your comment.
Your email address is not published. Required fields are marked with an asterisk (*)

*Name   *Email   Notify for updates
Comments
*Enter Code refresh code


 
Sponsor Information
"SQL doctor ROCKS! As soon as I ran it, problems that have been giving me headaches were identified and cured."

Web-based SQL Server monitoring whenever, wherever.

Make the most out of SQL Server - Guaranteed Results - Innovative SQL Server DBAs

Solving SQL Server problems for millions of DBAs and Devs since 2006. Join now.

Free Web Cast - Database development best practices by SQL Server MVPs Grant Fritchey and Jeremy Kadlec


Copyright (c) 2006-2012 Edgewood Solutions, LLC All rights reserved
privacy | disclaimer | copyright | advertise | about
authors | contribute | feedback | giveaways | user groups
Some names and products listed are the registered trademarks of their respective owners.


Edgewood Solutions LLC | MSSharePointTips.com | MSSQLTips.com