Write Advanced functions in PowerShell using various Write Cmdlets

PowerShell has become de-facto tool of choice for automation in Microsoft world from long time and slowly it is winning over hearts of the Linux administrators as well. Just like with other programming languages, there are many ways to do the same thing in PowerShell. However they differ in little subtle ways. You may or may not notice them in your day to day usage, but if you learn those subtleties, you can quickly improve the performance and results of your automation. This blog post is about one of the such cases only.

There are many ways to output the information in PowerShell using various Write- Cmdlets. We will be specifically discussing about below cmdlets:

  1. Write-Host
  2. Write-Output
  3. Write-Debug
  4. Write-Warning
  5. Write-Error
  6. Write-Verbose
  7. Write-Progress

About CmdletBinding()

We did not discussed about it anywhere above. It is important to learn first about cmdletbinding() and why it is very important to learn in PowerShell. Let’s say that you have written a function like below in PowerShell:

function Get-Sum($num1, $num2){
  return ($num1+$num2)
}

Now, let’s try to call this function in IDE. If we try to pass parameters to this function, there is almost no information on what needs to be passed:

Calling Get-Sum function to understand cmdletbinding

If you have noticed, this is a little bit different from what we have seen with the built-in PowerShell cmdlets. If you run any built-in PowerShell cmdlets, you will see below options:

BuiltIn Cmdlet Options in PowerShell

It turns out, achieving above is very easy. We need to only add couple of lines to our function:

function Get-Sum {
   [CmdletBinding()]
   Param(
      $num1,
      $num2
   )

   return ($num1+$num2)
}

Notice that we added [CmdletBinding()] in a new line and we moved paramters inside a special block named Param(). Now if we call above function, we would see something like:

Calling Get-Sum function after adding cmdletbinding attributes

What we just did was that we turned our function into what is called an advanced function that offers support for native cmdlet like functionality in your own functions.

Write-Host Cmdlet

Write-Host is the most commonly used and most commonly abused Cmdlet of various Write- Cmdlets. However this is probably the first cmdlet that you would learn in the PowerShell. It is used to write information to the host that you want the used of your script to see. The output information written using this cmdlet is always sent to host. For example,

Write-Host "Hello, World !!!"

Above code, will result in output like below:

Displaying message on console using write-host

It has other options like color support:

Write-Host "Hello, World !!!" -ForegroundColor Red -BackgroundColor Yellow

which results in output like below:

Displaying information on console using write-host cmdlet

One of the other important options is -NoNewline Switch which lets you print subsequent message in the same line. It is to be noted that Write-Host is host dependent. In the console, it is essentially doing [console]::WriteLine.

This cmdlet is very useful if you are running any script interactively.

Write-Output Cmdlet

This cmdlet is used to print information to the pipeline, so that it can be consumed by next cmdlet in the pipeline. If there is no cmdlet at the end of the pipeline, it sends the output to current output stream.

So below two commands are equivalent:

Get-Service 
Get-Service | Write-Output

Also, it sends the data across pipeline as .NET Objects. It is built-in into almost every PowerShell cmdlet. So you would rarely need to use it.

Difference between Write-Host and Write-Output Cmdlet

There are two important differences between Write-Host and Write-Output cmdlets:
First, Write-Host handles data as text but Write-Output handles data as Objects. Secondly, Write-Output sends data to the pipeline unlike Write-Host which sends data to the current host. For example, consider below code:

$var1 = "Hello, Write-Host" | Write-Host
$var2 = "Hello, Write-Output" | Write-Output

Checking output of above code would be something like below:

Understanding difference between Write-Host and Write-Output cmdlets

I have seen some people using Write-Output to return objects and would strongly argue against it. If you indeed need to return something as part of function execution, use explicit keyword return followed by the object that you want to return.

Write-Debug Cmdlet

Generally, you run your code without the Debug switch. However, there will be a time, when you want to break inside your funciton and step through the logic written one line at a time. If you are running with the -Debug switch, then control will break on the line of code that has Write-Debug statement, allowing you to step through code from that statement forward. For example, consider below code:

Function Get-NumberTable{
   [CmdletBinding()]
   Param( 
      $Number
   )

   1..10 | ForEach-Object { 
      Write-Host ($Number*$_)
   }
}

Get-NumberTable 10

Above code will let you print a table of 10. Let’s assume that we have made a mistake somewhere in the code and inserted a Write-Debug statement. Then if we call our function with -Debug switch, it would break at the statement:

Debugging functions by using Write-Debug Cmdlet

If the function is not called using -Debug Switch, nothing happens and function will be executed as usual. So Write-Debug cmdlet allows to occasionally break into function at the time needed.

Write-Warning Cmdlet

Write-Warning has a color coding that catches the attention of the user. Use this when you want to let the user know that something is not normal but the code has to continue anyway as it not a big enough problem to stop execution. I t is also useful in scenarios, where you would like to print out before performing a critical operation like deletion or overwrite.

For example, consider below code:

Function New-Directory{
   [CmdletBinding()]
   Param( 
      $Path
   )

   if(Test-Path $Path){
       Write-Warning "This path already exists"
   }
   New-Item -ItemType Directory -Path $Path
}

New-Directory -Path 'C:\System Volume Information'

which results in output like below:

Print Warning messages using Write-Warning Cmdlet

Write-Error Cmdlet

Write-Error is to display error messages to the users. The default behavior is to continue the execution after displaying error message. Write-Error has a lot of parameters that lets you customize it. The key among them is the Category parameter. For example, you could write a non-terminating error as shown below

Write-Error "Unable to connect to server." -Category ConnectionError

Although it used to signal non-terminating errors, you can in fact stop the execution by setting the environment $ErrorActionPreference as below:

$ErrorActionPreference = "Stop"

Write-Error Vs Throw

Throw is generally used when you want the program execution to stop (unless there was a try/catch to handle the error and resume from it).

Using try-catch block inside a function is generally overkill. If you need to stop program execution, you can set the $ErrorActionPreference to stop as specified above. Also, if you need to continue program execution, it is the default behavior or you can set $ErrorActionPreference to SilentlyContinue or just Continue.

Write-Verbose Cmdlet

This is the probably the most important and widely used cmdlet of all the ones discussed till now. Basically, this is all the extra information you want to see as your program is running if it was started with the -Verbose switch. So this command is very helpful in displaying all the extra information needed at the time of execution. If the function is called without -verbose switch, all Write-Verbose statements are skipped. For example, consider below code:

Function Get-NumberTable{
   [CmdletBinding()]
   Param( 
      $Number
   )

   1..10 | ForEach-Object {
      Write-Verbose "Current step is $_" 
      Write-Host ($Number*$_)
   }
}

If we call above function with -verbose switch, we should see below output:

Displaying additional information on console using write-verbose cmdlet

And, if we call above function without -verbose switch, we should see below output:

Write-Verbose statements are skipped

Write-Progress Cmdlet

Write-Progress cmdlet is used to display the progress of a particular section of code. This is very useful if you are performing long running operations. Write-Progress can even do nested loop progress. i.e., If there are three levels of nesting in loops, three progress displays may be done for each level of nesting. For example, consider below PowerShell code:

If we run above code, we should see something like below:

Displaying progress for long-running operations

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s