Write-Warning or Write-Error

Someone posted the following comment on my Scripting Games Event 1 entry

If I was kidding, I’d say simply reply because of the color scheme 😉
You can make errors less intimidating by using the technique described by Boe Prox in the following article: Make Your PowerShell Errors Less Harsh By Changing Their Color

This is actually a good question and more seriously let’s first look at what the help says about it:

Get-Help Write-Error

The Write-Error cmdlet declares a non-terminating error. By default, errors are sent in the error stream to the host program to be displayed, along with output.
[…]
Non-terminating errors write an error to the error stream, but they do not stop command processing. If a non-terminating error is declared on one item in a collection of input items, the command continues to process the other items in the collection.

Get-Help Write-Warning

The Write-Warning cmdlet writes a warning message to the Windows PowerShell host. The response to the warning depends on the value of the user’s $WarningPreference variable and the use of the WarningAction common parameter.

Behind this question, there’s something huge going on. It’s about how to handle errors gracefully. I borrowed this term from Don Jones because it really illustrates that it’s a question of spirit, philosophy and that you can precisely control the behavior of PowerShell and effectively instruct it what to do.

Let’s come to my entry
There a two try{}catch{} blocks in my function:

Begin {}            
Process {            
    Get-ChildItem -Path $Path -Directory | ForEach-Object -Process {            
        $Folder = $_            
        $TargetPath = (Join-Path -Path $Destination -ChildPath $Folder.Name)            
        # Ensure that the target folder exist before moving files            
        If (-not(Test-Path -Path $TargetPath -PathType Container)) {            
            try {            
                New-Item -Path $TargetPath -ItemType Directory -ErrorAction Stop | Out-Null            
            } catch {            
                Write-Warning -Message "Failed to create the directory $TargetPath because $($_.Exception.Message)"            
                break            
            }            
        }            
        try {            
            # Use the new File switch to avoid directories            
            Get-ChildItem -Path "$($Folder.FullName)\*.LOG" -File |             
            Where-Object { $_.LastWriteTime -le (Get-Date).AddDays(-$Age) } |            
            Move-Item -Destination $TargetPath -ErrorAction Stop            
        } catch {            
            Write-Warning -Message "Failed to move files because $($_.Exception.Message)"            
        }            
    }            
}            
End{}            

The first one around the New-Item cmdlet. New-Item produces a nonterminating error. I changed its behavior with the ErrorAction parameter, I told PowerShell to stop and what to do inside the catch block. The destination path parameter of the function was correctly passed and is available. But when creating the missing subfolder in the destination path, something wrong happened. I don’t know why exactly, it could be a permission issue or due to the fact that a file with the folder name already exists in this location. For me, it’s game over, it’s a non-sense to continue executing the subsequent code and try to move items. I prefer kindly issuing a warning and break the pipeline. The break keyword instructs PowerShell to exit its current loop level, the foreach-object construct in my case which is actually the whole process block. If you haven’t modified the default $WarningPreference variable, you see the warning. If something wrong happened while creating the target directory, there’s a problem that requires the user to investigate. The root cause of this exception is very likely to occur for the other directories, that’s why I chose to “exit” the function.

The second try{}catch{} block aims at capturing nonterminating errors of the Move-Item cmdlet. I don’t expect any issue to occur at this step as the instructions say that

Once created on disk, the files are never touched again by the applications.

But, the target folder may already exist in the destination path. There could still be some permissions issue introduced between now and the last time I’ve successfully used the function. If that happens, I want Powershell to stop, handle this nonterminating error as a terminating exception as well as instruct it what to do, i.e., please kindly inform the user that an exception occurred. He should look at the exception message to understand what’s wrong, take some corrective actions before executing the function again and move all the LOG files successfully. There’s no break and continue keywords in this second catch block. Ok, something wrong happened but I want PowerShell to keep trying with the files in the other subfolders. After all, maybe some files were just “locked” which (of course) shouldn’t happen based on my interpretation of the above instructions’ statement.

Why would I go with Write-Error ? Here’s what I tested:

Write-Error -Message "test"            
1 + "ab"            
            
try {            
    1 + "ab"            
} catch {            
    Write-Warning -Message "$($_.Exception.Message)"            
}            
            
try {            
    1 + "ab"            
} catch {            
    Write-Error -Message "$($_.Exception.Message)"            
}


Sorry, I still prefer the Write-Warning. The last two lines of the Write-Error don’t bring anything.
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

I feel that the output of Write-Error looks less graceful than the one of Write-Warning.
I don’t think that using Write-Warning is inappropriate or I didn’t understand the comment on my entry.

In PowerShell version 3.0, warnings have now their own stream. The warnings stream can be captured or captured and merged to the errors flow.
Although it’s not mentioned in the

Get-help About_Windows_PowerShell_3.0

You can read about it with

Get-help about_redirection

It isn’t well known. It saw it first in a word document of Powershell 3.0 CTP2

Advertisements

2 thoughts on “Write-Warning or Write-Error

  1. When I create my scripts I actually prefer to not use write-error or write-warning. I like having more control around how messages are perceived by the end user. Some “Errors and Warnings” are benign, while others may be something that you need to issue a BREAK command to halt execution.
    Try {
    1 + “ab”
    }
    Catch {
    write-host “!! Error Executing Command. Error Details: $_”
    }
    Essentially, the $_ will output the exception details. If the message is critical I will change the font or background color and BREAK the script. If its something that isn’t critical to the success of the script, I will just write the message to the console and continue.

    I do agree that write-error misses the mark for legibility and how the errors are perceived by users.

    • You know that write-host should be banned as it’s considered as painting messages to the console. If you do some automation and want someone else to reuse your code, you should consider using write-verbose, write-warning, write-error and write-debug and their stream can be captured.
      I’d recommend reading the best practices that the PowerShell community produced after the scripting games.

      You’ll find on http://powershell.org/wp/ebooks, two books

      • The Community Book of PowerShell Practices
      • The Big Book of PowerShell Error Handling

      that you can download from the onedrive using this link: http://1drv.ms/1eaLKiu

      Source: http://blogs.technet.com/b/heyscriptingguy/archive/2012/06/18/the-top-ten-powershell-best-practices-for-it-pros.aspx

      4. Do not use Write-Host. One of the great features of Windows PowerShell is that it is object oriented. This means that cmdlets return objects, such as Get-Process, which returns an instance of the System.Diagnostics.Process object. The great thing about objects is that they have lots of methods and properties. In Windows PowerShell, these objects flow along the pipeline. Using Write-Host interrupts the pipeline, and destroys the object. There are times to use Write-Host, such as producing status messages in different colors, but do not use Write-Host to simply write textual output. Instead, directly display the contents of variables, and write your strings directly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s