Feb 042011
 

My most popular article has thus far been the post over multithreading found here. As such I felt that my audience might like to see some more entries covering that topic. After all, it is a vast topic with a lot of very advanced things you can do. In this post I would like to talk about tracking the status of your various threads. In other words, how can you see a progress of what your child script is doing?

In exploring this myself, I worked with passing custom objects back to the parent which held various status messages that the parent could read in with the “receive-job” command. That actually worked, but it took a lot of code in both the parent and the child to generate and read the status from the child threads.

While working with this, though, I found that the Job object from “get-job”, a System.Management.Automation.Job, has a property called “Progress”. I then decided to look into how this actually gets filled out. And the answer eluded my research until I managed to do it, completely by accident. As it turns out, this very useful property is filled in automatically by a “write-progress” command run within the child thread. It is actually quite logical that the progress field is filled in by “write-progress”.

So, that is enough of a background on how I came about it. Let’s actually look at some code. First,I want to create a script block to run within my child job.

## Write some code for the child threads to execute
$ThreadCode = {
    $x = 0
    $time = 200
    While ($x -lt 100){
        $x++

        Write-Progress  -Activity "Testing"
            -Status "Testing"
            -PercentComplete $X
            -completed
        Start-Sleep -Milliseconds $time
    }
    Write-Progress  -Activity "Testing"
            -Status "Testing"
            -PercentComplete 100
            -Completed
    "Annnd we're done."
}

So here you’ll notice that I am basically creating a loop that will count 1 to 100 and then close. Along the way it will fill out its progress with the “write-progress” cmdlet. The one important note is that I am adding the “-completed” parameter to the “write-progress” cmdlet. That may look like a mistake in the code,but it isn’t. If you play around with this manner of getting progress from a child you will notice that when you run a “receive-job” your host thread will read all output from the child, including flashing all of the “write-progress” bars that were in the child. If you actually read the help files for “write-progress”, you’ll see that actual description of the functionality of “-complete” states that is simply hides the output bar. We need to do this so that when the parent thread runs a “receive-job” there is no odd behavior for the end user.

## Kill any current jobs in the sessions and get rid of them
Get-Job | Stop-Job
Get-Job | Remove-Job

## Start the child job and assign to a variable to save some code later
$Job = Start-Job -ScriptBlock $ThreadCode

So for this quick snippet we are just closing an remove any jobs our current session may have, and then starting our job. I assign it to a variable so we don’t have to track it down later, not that it’s hard.

While ($Job.State -eq "Running"){
    ## Get the Child job object
    $Child = $Job.ChildJobs[0]
   
    ## Get the newest Progress Object in the child job
    $Progress = $Child.Progress[$Child.Progress.Count - 1]

    ## If the progress object is not null
    If ($Progress.Activity -ne $Null){
        Write-Progress  -Activity $Job.Name
                        -Status $Progress.StatusDescription
                        -PercentComplete $Progress.PercentComplete
                        -ID $Job.ID
    }
   
    Start-Sleep -Milliseconds 200
}

So this is the bread and butter of the parent script. It simply loops around as long as our job is running. First we need to find the child job to our parent job. I am not sure why Microsoft chose to design it like this, but each job you start actually runs commands in a child job. I have a feeling it has something to do with functionality with Windows HPC, but that is neither here nor there. Once we have our child job, we want to find the latest progress object that has been passed. After that I just check to see if the object is empty, because the first check may not have given the child thread enough time to fill this out. If there is data, then we can fill out a “write-progress” bar on our host thread. From there we just repeat until the job is done.

Get-Job | Receive-Job

At the end we just want to read anything the child script may actually have sent. So that covers it. It is really easy to get custom progress bars for a child process because there is a good, built in method to do it.

Following is a full example of a script which will launch three child threads at a time until there have been 10 opened total. It will show you the output of each thread as it completes and the progress bars produced by each. I even included a random speed that each child will complete so you can see some bells and whistles going.

## Write some code for the child threads to execute
$ThreadCode = {
    $x = 0
    $time = Get-Random -Maximum 200 -SetSeed $(Get-Date).Millisecond
    While ($x -lt 100){
        $x++

        Write-Progress -Activity "Testing" -Status "Testing" -PercentComplete $X -completed
        Start-Sleep -Milliseconds $time
    }
    Write-Progress -Activity "Testing" -Status "Testing" -PercentComplete 100 -Completed
    "Annnd we're done."
}

## Kill any current jobs in the sessions and get rid of them
Get-Job | Stop-Job
Get-Job | Remove-Job

## Loop control
$x = 0

## While we are still launching threads or wait for them to close
While (($x -lt 10) -or ($(Get-Job -State "Running").count -gt 0)){
    ## While there are less than three jobs running and less than 10 have started
    While (($(Get-Job -State "Running").count -lt 3) -and ($x -lt 10)){
        Start-Job -ScriptBlock $ThreadCode | Out-Null
        $x++
    }
   
    ## Main portion - Read all jobs
    ForEach ($Job in Get-Job) {
        ## Read all children of all jobs
        ForEach ($Child in $Job.ChildJobs){
            ## Get the latest progress object of the job
            $Progress = $Child.Progress[$Child.Progress.Count - 1]
           
            ## If there is a progress object returned write progress
            If ($Progress.Activity -ne $Null){
                Write-Progress  -Activity $Job.Name
                                -Status $Progress.StatusDescription
                                -PercentComplete $Progress.PercentComplete
                                -ID $Job.ID
            }
           
            ## If this child is complete then stop writing progress
            If ($Progress.PercentComplete -eq 100){
                Write-Progress  -Activity $Job.Name
                                -Status $Progress.StatusDescription
                                -PercentComplete $Progress.PercentComplete
                                -ID $Job.ID
                                -Complete
                ## Clear all progress entries so we don't process it again
                $Child.Progress.Clear()
            }
        }
    }
   
    Get-Job -State "Completed" | Receive-Job
   
    ## Setting for loop processing speed
    Start-Sleep -Milliseconds 200
}

Get-Job | Wait-Job | Out-Null
Get-Job | Receive-Job
Feb 022011
 

As you are typing in any given PowerShell console or in PowerShell ISE you can finish typing many things simply by hitting the “Tab” key while you are halfway through a word. This practice is refered to as “Tab Complete”. If you aren’t in the practice of using this all the time, then you really need to start utilizing it heavily for your own sake. Not only can it help you type and create code faster, but it can be useful in remembering cmdlet names or parameters when you are typing.

Things I know you can tab complete are: cmdlet names, file and directory names, third party programs, custom scripts, cmdlet parameters, variable names, object members, functions, and many more.

Another cool thing is that if you hit it too early in the word, simply hit it again to keep going through all possible entries. If you type “Get-” and then start hitting tab,you will see the names of all the cmdlets until you get the one you want. Or after you’ve typed the name of a cmdlet,type “-” and then tab to see all possible parameter names, and it even works with your own scripts!

Skipped by the command you wanted? Shift-Tab will start you back in the other direction.

Feb 022011
 

When you are trying to run a script over a very large environment you often want to make sure that each machine you are trying to hit is accessible and that you have permission. Most importantly, however, you want to do this as quickly as you can. This got me to thinking. I can use the test-connection cmdlet to see if the machine is online. It’s a really good tool because it can be passed the “-quiet” parameter and is then set to return a simple true or false answer. As well, you can speed it up by passing the “-count” parameter and speed things up by only testing once.

That’s easy enough, but what is the best way to test for permissions, and quickly. I’ve given it a lot of thought, and I think the best way is to run a test-path to the admin$ or c$ shares. Using these built-in shares you can quickly verify if you have administrative privileges to the machine. Here’s the catch, if you try to run the test-path cmdlet against a machine that isn’t responding it can run very slowly. So we want to first verify that it’s on, and then test for privileges. To guarantee that the test-path wouldn’t run I had traditionally made two “IF” statements with one nested into the other.

If (Test-Connection $Machine -quiet -count 1){
    If (Test-Path $Machineadmin$){
        #Code Here
    }
}

But I thought that made for some rather ugly code. Instead I wanted to put both tests into a single “IF” statement, but I was worried that both commands would run and slow everything down. Logic states that it would be rather pointless to test for the second condition of an “AND” statement if the first condition was false. So I thought that maybe PowerShell would cut out the second portion, so I write this quick script to test my theory.

Function A{
    write-host "Running A"
    Return $False
}
Function B{
    write-host "Running B"
    Return $True
}

If ((A) -AND (B)) {
    "Found True"
}Else{
    "Found False"
}
>>Running A
>>Found False

After playing with that code some more with various conditions I found exactly what I was hoping for. If you are testing multiple code blocks with an “AND” statement then it will execute down the path until one is false, and then stop.

So now I have shortened my test code to just one “IF” statement.

If ((Test-Connection $Machine -quiet -count 1) -AND (Test-Path $Machineadmin$)){
    #Code Here
}

As far as speed goes,it will of course depend on your environment,but I found that the following times were averages in mine:

Machine doesn’t exist (No DNS): 2500 ms
Machine not online: 3800 ms
Permissions denied: 50 ms
Online and Accessible: 30 ms