Author Posts

December 10, 2013 at 10:24 am

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

December 10, 2013 at 10:55 am

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.

December 10, 2013 at 10:55 am

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
}

December 10, 2013 at 11:10 am

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

December 10, 2013 at 11:13 am

If your tasks are in separate script files, sure.

December 10, 2013 at 11:21 am

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

December 10, 2013 at 12:09 pm

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

December 10, 2013 at 12:16 pm

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.

December 10, 2013 at 12:21 pm

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.

December 10, 2013 at 2:37 pm

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.

December 11, 2013 at 4:56 am

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

December 11, 2013 at 6:34 am

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