Powershell script designing

This topic contains 11 replies, has 3 voices, and was last updated by Profile photo of Dave Wyatt Dave Wyatt 3 years, 6 months ago.

  • Author
    Posts
  • #11918
    Profile photo of Bin Ary
    Bin Ary
    Participant

    hello
    i want to wrap or encapsulate three tasks (scripts) sequentially..is it possible and best parctise to do like that

    #script task1
    [cmdletbinding()]
    Param( $var 1=...,
    $var2=....)
    #Function task2 definition
    function task2
    {
    ......
    }
    #Function task3 definition
    function task3
    {
    ......
    }

    # script block for task1
    .....
    .....
    #call function task2
    if (error.count -eq 0) {task2}
    #call function task 3
    if (error.count -eq 0) {task3}

    # End script block for task1

    thank you in advance

  • #11920
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    That would work. The other way – and the way I would normally use – is to create a module containing the three task functions and a controlling function.

  • #11921
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    You could do that, but keep in mind that the $error variable is not guaranteed to be empty when your script starts. You could either clear it yourself, or use ErrorVariable in your function calls (make sure each of the functions has the [CmdletBinding()] or [Parameter()] attributes somewhere, so they will be advanced functions that accept the common parameters.)

    For example:

    Execute-Task1 -ErrorVariable err
    if ($err.Count -eq 0) { Execute-Task2 -ErrorVariable err }
    if ($err.Count -eq 0) { Execute-Task3 -ErrorVariable err }
    

    Another option would be to put all three tasks into a Try block in conjunction with ErrorAction Stop (to make sure any errors are terminating errors); if an error occurs, the subsequent steps don't run.

    try
    {
        Execute-Task1 -ErrorAction Stop
        Execute-Task2 -ErrorAction Stop
        Execute-Task3 -ErrorAction Stop
    }
    catch
    {
        throw
    }
    
  • #11922
    Profile photo of Bin Ary
    Bin Ary
    Participant

    hello
    you mean i create one file script globalscript.ps1 with these line

    try
    {
    c:\Execute-Task1.ps1 -ErrorAction Stop
    c:\Execute-Task2.ps1 -ErrorAction Stop
    c:\Execute-Task3.ps1 -ErrorAction Stop
    }
    catch
    {
    throw
    }

    thank you

  • #11923
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    If your tasks are in separate script files, sure.

  • #11924
    Profile photo of Bin Ary
    Bin Ary
    Participant

    module is a good idea also...thanks for the advice

  • #11925
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    Doing this isn't a good idea

    try
    {
    c:\Execute-Task1.ps1 -ErrorAction Stop
    c:\Execute-Task2.ps1 -ErrorAction Stop
    c:\Execute-Task3.ps1 -ErrorAction Stop
    }
    catch
    {
    throw
    }

    Why?

    Because you don't know where the errors are coming from.
    When you are using try-catch blocks you want to have the minimum code in the try block. This enables you to handle the errors in a more specific way that is tailored to the commands you are running.
    Your code should look more like this:

    ## task 1
    try
    {
    c:\Execute-Task1.ps1 -ErrorAction Stop
    }
    catch
    {
    throw # mesages relevan to task 1
    }
    ## task 2
    try
    {
    c:\Execute-Task2.ps1 -ErrorAction Stop
    }
    catch
    {
    throw # mesages relevan to task 2
    }
    ## task 3
    try
    {
    c:\Execute-Task3.ps1 -ErrorAction Stop
    }
    catch
    {
    throw # mesages relevan to task 3
    }

    Remember that you can have multiple exceptions to catch on and they can be different between the 3 tasks if needed

  • #11926
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Just depends on what your requirements for the error handling / reporting are. If all you're doing is rethrowing the error (as in my example), there's no advantage to having three separate try/catch blocks rather than one. If you're wrapping the error in a new object or reformatting it in some way (as your (throw # mesages relevant to task 2) example seems to imply), then it's better to have a separate try block for each task.

  • #11927
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    As a best practice you should always have the minimum code in your try block. if you aren't going to be using the exception catching then why bother with it in the first place.

  • #11938
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    In PowerShell, you can use try { whatever } catch { throw } to enforce consistent behavior from different sources of terminating errors; by default, some of them cause your current script or function to abort, and others just output the error and move on to the next statement. This isn't necessary in other languages where any unhandled error automatically kills the execution of your current scope and passes the error back to the parent. PowerShell's fairly unique in that sense, and as a result, many of the best practices that we inherit from C# or Java don't always apply.

  • #11945
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    The PowerShell try-catch block is based on C# in that you try something and if a .NET exception is raised you can manage it in the catch block.
    To that end minimising the number statements within your try block enables your catch block to specific in its exception handling

  • #11947
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    I understand that, and agree with it, if your intention is to actually handle a specific error from a specific statement.

    My point is that PowerShell behavior is not consistent when it encounters an unhandled terminating error. Depending on how that terminating error was generated, sometimes PowerShell aborts the execution of your current scope (similar to an unhandled exception in C#), and sometimes it continues execution with the next statement. You can use try/catch to enforce the first behavior, if that's what you want to happen. For example:

    try
    {
        Statement1
        Statement2
        # ...
        StatementN
    }
    catch
    {
        throw
    }
    

    This has nothing to do with handling a particular error; it's about making sure that PowerShell aborts your current function if any of those commands produce a terminating error that you haven't handled (which can be done with smaller try/catch blocks inside the large Try block, if you like.) This is one of the little PowerShell quirks covered in the draft of the Error Handling ebook that I'm working on.

    Here's an example of a situation where PowerShell will continue execution even after a terminating error. I happened to be looking at the Add-Type cmdlet in DotPeek this morning, so it's fresh in my memory that it throws a terminating error if both the Language and CodeDomProvider parameters are passed:

    Function Test-Function
    {
        [CmdletBinding()]
        param ( )
    
        $provider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider('CSharp')
    
        'Statement before error.'
    
        Add-Type -Language CSharp -CodeDomProvider $provider -TypeDefinition 'public class TestClass { public int TestProperty; }'
    
        'Statement after error.'
    }
    
    Test-Function
    

You must be logged in to reply to this topic.