Friday, November 13, 2015

New no new line in PowerShell 5

To write information to a log file, we can use Out-File, Set-Content, or Add-Content. (There are, of course, other ways, but this article is about a new feature in those three.) Up to now, it has always been challenging or at least ugly when you wanted to slowly build a lot information into a single line, because you could only write an entire line at a time.

You may have had to store a line in a variable until it was complete and then write it to the file. Or you might have had to do something like this.

001
"$(Get-Date) - Computers accessed: $($Computers.Name -join ", ")" | Out-File $LogFile -Append

In PowerShell 5, a new parameter was added to Out-File, Set-Content and Add-Content, -NoNewLine.

So now, we can do something like this:

001
002
003
Get-Date                   | Out-File $LogFile -Append -NoNewline
" - Computers accessed: "  | Out-File $LogFile -Append -NoNewline
$Computers.Name -join ", " | Out-File $LogFile -Append

(Yes, it's a stupid example. But you see what I'm getting at.)

And of course it works with the other ones as well.

001
002
003
$X | Out-File    $LogFIle -NoNewline
$X | Set-Content $LogFile -NoNewLine
$X | Add-Content $LogFile -NoNewLine

Thursday, November 12, 2015

New functionality for working with temp files in PowerShell 5

Sometimes you need to store data in a script. If you decided to stick it in a temp file, in the past you had to figure out where to create the file, confirm the location exists at runtime, figure how to name the file, and handle possible name conflicts. Or maybe you just decided not to use a temp file just so you could avoid those complications.

In PowerShell 5, we have a new command that handles all of the automatically.

New-TemporaryFile

New-TemporaryFile takes no parameters (other than “common” parameters), so it’s very simple to use.

It gets the location of the temp folder for the current user. If the script is running in the context of a user, this will be a user-specific folder. The exact location will vary from one OS version to another.
If the script is running under the local SYSTEM or NETWORK account, this will be the machine temp folder.

It creates an empty file with a name like tmpD435.tmp. The four random hex characters are guaranteed to be unique in the folder. You can create over 65,000 of them in your temp folder before running out of names.

It returns a fileinfo object giving you the details on the file it created.

So let’s say we want to have a script that dumps details to a temp file, and then either deletes the file if the script is successful, or opens it for the person running the script to see the details. It would look something like this.

001
002
003
004
005
006
007
008
009
010
011
012
013
$LogFile = New-TemporaryFile
## Code
"Log entry" | Out-File -FilePath $LogFile.FullName -Append
## More code
"Another log entry" | Out-File -FilePath $LogFile.FullName -Append
If ( $Success )
    {
    $LogFile | Remove-Item
    }
Else
    {
    Start $LogFile
    }

Wednesday, November 4, 2015

Improve PowerShell 5 script performance with class

Sometimes PowerShell scripts run a little slow. In many cases, you can speed them up by adding a little class.

PowerShell 5 introduced native class definition. At first glance, this is rather advanced, and not something most scripters will need to implement. But I discovered something that may make it useful even when you don’t need it. It’s fast.

PowerShell is kind of slow. That was part of the trade off at the core of its design. Because it not pre-compiled, it  is slow, but we can use it at the command line, and we can mix and match command line and scripts and pieces of scripts.

The difference in the performance of various pieces of code can have less to do with the speed of what you think it's doing, and more do to with the amount and efficiency of the overhead PowerShell needs to do it. For example, PowerShell gives us a lot of flexibility with how we work with variables, which comes at a cost of lots of overhead at run time for it to be able to handle any variable weirdness we throw at it.

When a PowerShell script is compiled at run time, code embedded in a custom class definition is compiled more efficiently, and requires less overhead to run. And then when you call that code, it can run much faster.

How much faster varies greatly from one chunk of code to the next.

For example, when I tested it using Measure-Command on an old virtual machine, this code ran in 20.6 seconds.

001
002
003
004
005
006
$x = 0
ForEach ( $i in 1..10000000 )
    {
    $x = $x + 1
    }
$x

The same code defined and called as a method of a custom class, took only 1.7 seconds to run.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
class MyMath
{
static [int] CountRealHigh()
    {
    $x = 0
    ForEach ( $i in 1..10000000 )
        {
        $x = $x + 1
        }
    return $x
    }
}

[MyMath]::CountRealHigh()

Other CPU intensive code I tested also saw improvements, though not all of it was as impressive as the first test.

This code ran in 13.0 seconds.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
function IsSemiPrime ( [int]$X )
    {
    If ( $X -le 1 ) { return $False }
    $Divisibles = 0
    ForEach ( $Divisor in 2..[math]::Sqrt( $X ) )
        {
        If ( $X % $Divisor -eq 0 )
            {
            $Divisibles++
            If ( $Divisibles -gt 1 )
                {
                return $False
                }
            }
        }
    return ( $Divisibles -eq 1 )
    }

1..100000 | Where { IsSemiPrime $_ } | Select -Last 1

While the same code in a custom class only took 10.6 seconds to run.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
Class MyMath
{
static [int] IsSemiPrime ( [int]$X )
    {
    If ( $X -le 1 ) { return $False }
    $Divisibles = 0
    ForEach ( $Divisor in 2..[math]::Sqrt( $X ) )
        {
        If ( $X % $Divisor -eq 0 )
            {
            $Divisibles++
            If ( $Divisibles -gt 1 )
                {
                return $False
                }
            }
        }
    return ( $Divisibles -eq 1 )
    }
}

1..100000 | Where { [MyMath]::IsSemiPrime( $_ ) } | Select -Last 1

Of course, most of our scripts aren’t just doing math. So our bottlenecks are usually other things, like waiting for connections, or for restarts, or for our applications to respond, or whatever. So most of our scripts won’t benefit from this. And even when they do, most of the time we don’t care if our scripts take a few second longer to run. A slightly slower script is usually worth the trade off in time saved not having to write, test, and maintain faster but more complex code.

But sometimes there is a pressing need for a speedier script. When the need arises, try wrapping your script or functions in a class definition.

Thanks to Bruce Payette at Microsoft for some of the behind the scenes details on how this works. Bruce is working with Richard Siddaway on the PowerShell 5 version of his book, PowerShell in Action. If you buy it in advance, you get access to chapters as soon as they are written. It's a bit expensive, but if you sign up for email, they send out discount codes every Friday. I highly recommend it.