Pages

Monday, November 4, 2013

What Do Until loops do for you

Lately I've been getting into a lot of multi-processing and threading features of powershell. Of course, being the Powershell Pancaker, I don't typically like most of the examples I see - as oftentimes they are just too bloated, and they hurt to read (variables in the ISE burn my retinas). But I really needed this. Almost as bad as I need coffee, or excercise, or underpants.

Have you ever wanted something to happen, until it happened correctly? What if you had a bit of code, that depends on something external, maybe you want to run a script, and then (for whatever reason) go manually complete a task, and the script will continue when you are done? I don't know why you would do that. But this can be particularly useful in scenarios where maybe you are waiting for some bit of AD replication, as I was, which prompted me to write this. This is essential if you are running jobs in Powershell.

The lazy mans way out of this predicament is to use the Start-Sleep cmdlet. Which basically means you have way too much free time to run this script, and obviously you really just don't care about pretty much anything. But this doesn't work if you have a deadline, and you need this to be done as fast as possible. And doubly so if you are running your function as a seperate job, where you either arn't particularly bothered to report results from each job or - it is just unnecessary. This also doesn't work if you need to execute 400 instances of Powershell and you only have 16GB of RAM. This could be troublesome if your lazy Start-Sleep timer wasn't long enough, and unbeknownst to you, your script failed or didn't meet your success criterion.

So let's get started.

Pancake:
DO { #Start a DO loop
    $a++
    Write-Host $a
    } Until ($a -eq 10) #Write $a++ until -eq 10

This is the basic breakdown. Until (...) accepts all sorts of input. This command is useful in a number of scenarios. Inside of the DO loop, you can do pretty much anything you want. Let's try something completely non-sensible.

DO {
    Start-Sleep 2
    Write-Host Waiting for folder..
    $Folder = Test-Path C:\PowerShellPancakes\Testdir
    } Until ($Folder -eq "True")

In the above, we are using one of the unlikely scenarios that you want to manually create a folder to return some sort of output. Let's say we need to wait until this folder is created, and then when it is, copy something into it? If you are interleaving copy jobs maybe you will find this useful. Otherwise no one else will ever use this. But these are Pancakes, remember? We can put whatever we want on them. To do this we just have to add whatever we want at the end of the loop to continue on with our script.

DO {
    Start-Sleep 2
    Write-Host Waiting for folder..
    $Folder = Test-Path C:\PowerShellPancakes\Testdir
    } Until ($Folder -eq "True")
Copy-Item -Path C:\PowershellPancakes\AuntJemima.txt -Destination C:\PowershellPancakes\testdir
#Script continues as normal

As you can imagine, we can use this bit of code to transform basically anything that requires a wait time, success criteria, value limit, into something of a more fire and forget system. Is there anything else we can do with this? Hmm, what about Try and Catch? This is what you need to know if you don't want a bunch of error output while you are waiting for your criterion to become true, or, whatever it is you are doing. Let's use the following example.

DO {
    Start-Sleep 2
    Write-Host Waiting for folder..
    $Folder = Move-Item C:\PowershellPancakes\testdir -Destination C:\PowershellPancakes\Destination
        } Until ($Folder -eq "True")

This is going to be super annoying. You haven't even created TestDir yet, how are we going to move it if it doesn't exist? It's okay. Powershell will definitely let you know that it doesn't exist. Every two seconds, actually. What if you aren't as stupid as it assumes you are, and you want it to chill out until you have created this folder so it can be moved. (Again. Let's forget practicality for a moment.) Well, this is where catch and try come into scope. With try, we can do just that - try things. There is a big difference between try and do, except in Powershell, we do try. (See what I did there?) So let's look at something that won't hurt our eyes quite so much.

$ErrorActionPreference = "Stop"
DO {
    Start-Sleep 1
    Write-Host Waiting for folder..
    try {
        $GetFolder = Get-ChildItem C:\PowershellPancakes\Testdir
        }
    catch { }
        } Until ($GetFolder -eq $null)
Copy-Item -Path C:\PowershellPancakes\AuntJemima.txt -Destination C:\PowershellPancakes\testdir

Ah, much better. Thank you for understanding Powershell, don't be so impatient. There is one important thing to note, however - try and catch work great, however only for terminating errors. The above is an example of a non-terminating error. The script will still go on. So that is why we need to set our $ErrorActionPreference variable. In the above we are doing exactly what we want, in the do loop we are going to try and get the contents of that folder, and we are going to keep trying every two seconds. However, with catch, we are capturing what comes out of that try statement. You can enter a lot of things in here, you can enter some additional processing, maybe a nice Write-Error that looks better, put it out to a log file, whatever - the possibilities here are endless. You can even use the system classes to catch particular types of errors from try, if you happen to be running more than one set of commands. When I write scripts I usually leave this blank as I haven't had a reason to fill in the catch block yet, I use it to just ignore the error output. But much like If and Else statements, they follow eachother naturally.

That's all for now - this should get you started with the Do Until, Try and Catch statements.