PowerShell: Enhance a CMDLET (Proxy/Wrapper)


I have this task to do, and well, I’m easily side tracked.

I find that I often times (especially with WMI calls) do a count and a Get-Member on the return just to see what I’m working with. So I thought, why not simplify that task?

function dwmi {
$rtn = iex "gwmi $([string]$args)"
$rtn | gm | ?{$_.name -notlike "__*"} | out-host
write-host "Count: $($rtn.count)"
return $rtn
}
$share = dwmi win32_share

This is a good example of where to use Invoke-Expression (IEX) as well as a good use of $args rather than named params. You could have this function use your own args as well, you’d just need to rip them out of $args before passing them to the CMDLET you are wrapping.

Just thought this was neat and worth sharing, hope you enjoy.

PowerShell: Microsoft Releases Script Explorer! Here is the ISE Fix


Microsoft has a pre-release of the Script Explorer which allows you to search poshcode, technet gallery as well as local repositories. Its kinda neat.

http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=29101

It ties in to ISE but not so nicely, however, with most things PowerShell, you can fix that.

This will work for a 64 bit system, I think you can easily see the change if you have a 32 bit system.


$filepath=  'c:\Program Files (x86)\Microsoft Script Explorer for Windows PowerShell\Microsoft.ScriptExplorer.ps1'

$content = gc $filepath

$content[4] += " | out-null"
$content | Out-File $filepath

No more spew on the screen!

PowerShell ISE addon for “Running” tabs


If you are like me then you have multiple PowerShell tabs open when you are working, partly to keep organized and partly to run longer tasks. I often scan all of my servers for something which can some times be a lengthy process. Rather than going back and forth between tabs to see if the job has completed I wrote this little script that you can toss in your profile that will change the tab name.

Register-ObjectEvent $psise.CurrentPowerShellTab PropertyChanged -Action {
If($Event.SourceArgs[1].PropertyName -eq "StatusText")
{ $tab = $event.Sender $name = $tab.displayname
if($Event.SourceArgs[0].StatusText -like "Running*")
 { $tab.displayname = "* $name" }
elseif($Event.SourceArgs[0].StatusText -eq "Completed" -or $Event.SourceArgs[0].StatusText -eq "Stopped")
{ $Tab.DisplayName = $name -replace "\* " }
 } }

Its up on Technet as well

http://gallery.technet.microsoft.com/PowerShell-ISE-Running-876cc74c

Enjoy!

PowerShell: Loading and Unloading Registry Hives


PowerShell will by default expose your HKLM and HKCU hives via drives which work because of the Registry PSProvider.


get-psdrive

get-psprovider

Since we see that it’s the provider that allows us to map these hives we can take it a step further and map a hive from a file (update user hives on a remote system). The problem with this is that the Registry PSProvider doesn’t extend to files. However this doesn’t stop us.


reg load 'HKLM\TempUser' $ntuserlocation

cd hklm:\TempUser

gci

New-PSDrive -Name HKMyUser -PSProvider Registry -Root HKLM\TempUser

cd HKMyUser:\

gci

cd c:

Remove-PSDrive HKMyUser

reg unload hklm\TempUser

This all works great until we attempt to unload that hive file or in some cases the unload works ok but we still have handles to the hive file (you can use sysinternals Handle.exe to see this)

Why is that if we removed the drive and asked Reg.exe to unload the hive? The problem is that the system has not released the memory which still has pointers in to that file, preventing us from unloading the hive or stopping us from doing other things.

So whats the trick you ask?

Ask the system to clean up those references that are no longer in use.


[gc]::collect()

This uses the static method Collect from the GC class in .NET which is used for forcing the garbage collector to run and removing those unused references.

PowerShell and Executables


I don’t normally do posts like this, but I just created a Technet Wiki on PowerShell and executables. The only reason I created it there and didn’t do the post myself is that I think there is a lack of info on the topic and a community driven page might be of great help here. So, if you can, please populate it with more info!

http://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx

 

PowerShell: Getting Started with Win32 API


Blog on Request!

Someone asked me to delve in to Win32 API use in PowerShell. Personally I don’t feel that I’m the best person to explain this, but I’ve done it a few times now so I think I have a decent handle on it.

Let me just start off by saying Win32 API should be your last choice (ok, second to last, I hate COM). You should do a very through search of the .NET framework before jumping in to the Win32 API pool.

Once you’ve exhausted all other options and have ended up in the Win32 world, what do you do? I’m not going to go over how to find the right WIN32 API for the task because if you are at this point, you’ve probably already found the API and just want to know how to use it.

First off load up the MSDN doc for it and the PInvoke.net doc. Usually googling the API will land you on those two pages pretty quickly.

Now for an example lets take a look at an API I’ve recently been playing with, GetSystemFileCache (pinvoke)

The Signature that MSDN gives us looks like this.

BOOL WINAPI GetSystemFileCacheSize(   __out  PSIZE_T lpMinimumFileCacheSize,   __out  PSIZE_T lpMaximumFileCacheSize,   __out  PDWORD lpFlags );

Which is the signature you’d use if you were coding in C++.  Since there is no direct way to use this in PowerShell we’re going to embed this in C# code and then consume that code in PowerShell. In many cases I’ll write the C# console app first and then apply that to my PowerShell script. This is what I did with my NetStat script, which was fairly complex. In this case we’re just calling a simple method to pull the current Cache limits.

Lets take a look at this Signature. The first thing shown is BOOL, which is the return type which the docs show is any nonzero number for success. Easy enough. bool is the same in C++ as it is in C#.

Next is WINAPI which is just a tag to show its a win32 api, this can just be removed.

After this is our method name, GetSystemFileCacheSize, easy enough.

Now this is where it gets a little tricky because we often times need to convert C++ types to something C# knows.

We see we have 3 arguments here, all 3 of which are prefixed with __out, which means they are output variables (If you don’t know how to program that well it’s basically passing a reference to a variable to return multiple things). So anytime you see __out, that means the variable needs to be passed in as a reference. We’ll get to that in a moment. Next is the type that the variable needs to be. Two of which are whats called PSIZE_T and one is a PDWORD. In both cases these are prefixed with a P, which indicates Pointer (pointer, reference, __out – all the same thing) so really the type is SIZE_T and DWORD.

How do we know what SIZE_T and DWORD are in C#? Google is really your best option here, or hopefully PInvoke.net has a signature you can use. In this case PInvoke does have it, but lets pretend it doesn’t. When we google ‘C# dword’ we get some good results that let us know this is an unsigned Int (UInt32). Another clue we have is that in the MSDN docs it tells us that it’s a flag and it can have a value of 0x1 or 0x4, both of which fit in to an int just fine. So, which do we use? It doesn’t matter, provided all possible return types can fit in.

Next is that Size_T, again googling ‘C# SIZE_T’ gives us some good info as does PInvoke, but lets just google SIZE_T.

Doing so provides a great data type reference.

We can see that PSIZE_T is in there but it just says it’s a pointer to SIZE_T which we already knew.

“The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer.

This type is declared in BaseTsd.h as follows:

typedef ULONG_PTR SIZE_T;

So this says it’s a ULONG (unsigned long, a long in C# is Int64, which google will help with again) It also indicates that it’s a pointer. This is where PInoke really saves us. It lets us know about an IntPtr type that is a special pointer specifically for this.

So now our new C# signature is as follows

bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

We see that it specifies ref for all the __out variables (in this case all of them, but some times they are __in) and a data type of IntPtr and int.

We’re still in the C# side of the world. we’ll need to use the System.Runtime.InteropServices (otherwise known as pinvoke). If you don’t know C# this might be a bit tricky, but we’re going to write a small C# class to contain our method and create a wrapper function around it. Not always needed but I find it makes it easier to work with.


using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

The first part of this is just adding the pinvoke namespace and the basic system namespace which helps with the IntPtr. We then create a public class. I usually start with Test1 because once you add a type to PowerShell you can’t overwrite it.

Then you’ll see the keywords [DLLImport(“kernel32.dll”)] so depending on what DLL your function is in will determin what you put in there which is followed by the signature.

Next I created a struct to help with the output to PowerShell. You’ll see how this comes in to play as we start to work with the PowerShell side of things. For now just see that it’s a simple holder for our return data. Next is our function wrapper. I’ve called it GetSize which returns my struct. You’ll also notice that it’s prefiexed with public static for both, which is important for accessing it within PowerShell.

In my function I set my min and max pointer to zero (special pointer zero) and the flags to zero. I then call the win32 api passing it the variables I created as references (pointers) and capture the return in b which if I was trying to do something for production, I’d check the return value.

I then create an object of my type CacheSize and populate it with the return values and return that to the caller.

Now that the C# is done, how do we apply this in PowerShell?

If you read my blog you’ll notice I’ve used Add-Type a few times which is a great tool for adding in types or C# code as we are doing here so we’ll just wrap this in a here-string and pass it to Add-Type like so:


Add-Type

<span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;">@'</span></span></span>

using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

}

'@

NOTE: Be careful of new lines in this code, the code formatter has been doing weird things to me lately.

Once you run that its imported the class in to our running environment and we can access that method like so.


$rtn = [test1]::getsize()

$rtn

$rtn | select @{n='min';e={$_.min /1mb}}, @{n='Max';e={$_.max/1gb}},flags

The return should be the same on all systems (at least what I’ve seen from tests) as 1MB min and 1024GB max. If you have a file server that’s hosting TB’s of data, you might find this to be a problem when its eaten all of your memory in system cache!

There is a way to set it with a win32 API that im working on now. I’ll be sure to post that solution when I figure it out!

I hope this has helped you understand how to use Win32 APIs a little better!

PowerShell: Custom Objects with Format Files


I was recently working on something and realized that the default output (everything) was getting rather annoying. I then realized that the object I had wasnt going to cut it and I had to create my own. This led to two problems:

1. I needed to get formatting for my data and since I was going to post the script online I didn’t want to make it a multi file solution. I wanted it so the end-user could just copy and paste the entire thing in to one file and run it.

2. In knowing that I needed custom formating I realized the normal PSObject wasnt going to cut it.

Step 1 (Item 2) The Custom Object

This part isn’t really all that hard, but its something I thought I would show. If you know C# this will make perfect sense, if you don’t, it will likely still make perfect sense. In order to have a type name with our object we have to define it via a C# class (Other .NET languages work, but I know and like C#.) You can make this as simple or as complex as you like but for this example its going to be very simple. We’re going to make a Contact object.

add-type @'
namespace JRICH
{
    public class Contact
    {
public string FName;
public string LName;
public string DisplayName()
        {
return (this.FName + " " + this.LName);
        }
    }
}
'@

There are a couple of note worthy items here. First you’ll see I’ve provided a namespace for this class. This isn’t needed but I find it to be good practice to help prevent duplication of class names. Also you’ll want to be weary of case in the C# side of things because it is case-sensitive. Along those lines you’ll notice that each line ends in a ;. As I said if you know C# this is no big deal, but if you are a scripter/admin learning PowerShell these are important notes.

One other thing I’ll say here is that if you are not used to C# and you plan on doing this you might want to grab a copy of Visual C# Express which you can download for free. The debugging and Intellisense will really help with learning C#.

Another reason to use Visual C# is that when creating a class in PowerShell once you add the type there is no way to remove it or overwrite it so after you add the type and decide you want to change it, you’ll need to close PowerShell and open it again.

In this case I’ve created a class called Contact which has a first name, last name and a display name method to give a friendly view of this.

Now that we have this custom object lets use it.

$contact = new-object jrich.contact
$contact.fname = "Justin"
$contact.lname = "Rich"
$contact
#FName  LName
#———————–
#Justin Rich
$contact.displayname()
#Justin Rich

Pretty easy huh?

Now that we have an object to work with lets put it in a script. Lets make a function that returns a handful of users. I’m making this overly simple but I think you’ll see how to apply this in your own scripts.

function Get-MyUsers ([string[]] $User)
{
    Begin
    {
        $results = @()
    }
    Process
    {
foreach($usr in $user)
        {
$tmp = new-object jrich.contact
$tmp.lname, $tmp.fname = $usr.split(',')
$results += $tmp
        }
    }
    End
    {
        $results
    }
}
Get-MyUsers @("rich,justin","smith,john","doe,jane")

Shortcut: Ctrl+J will bring up the snippet menu (in ISE) which will allow you to generate a function body very quickly.

Step 2 (Item 1) The Format File

Our simple function is all well and good but lets say we are unhappy with the default output format and want to change it so the user doesn’t have to do it every time. In order to change the default display we need a Format File. The Help system has About_format.ps1xml and MSDN has pretty detailed documents on it, but lets just say its fairly complex. Thankfully James Brundage over at Start-Automating.com has created EZOUT, a very nice tool to help us create those format files.

Im not going to go in to details on how to create these files or use that tool since there is a very nice YouTube video for it.

Back to our function. Lets say we’d like to make the default view be the return of the Displayname method.

This is the XML that the EZOUT has spit out for us.

xml version="1.0" encoding="utf-16"?>
<Configuration><ViewDefinitions><View>
    <Name>jrich.contact</Name>
    <ViewSelectedBy>
        <TypeName>jrich.contact</TypeName>
    </ViewSelectedBy>
    <TableControl>
        <TableHeaders>
            <TableColumnHeader><Label>DisplayName</Label></TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
$_.displayname()
                </TableColumnItems>
            </TableRowEntry>
        </TableRowEntries>
    </TableControl>
</View></ViewDefinitions></Configuration>

Typically what you would do is save this file and use the Update-FormatData cmdlet to import this in to PowerShell’s current session environment.

In our case, because we don’t want to have more than one file we want to embed this in to our script. Since the only way to get this format data in to the environment is to use the Update-FormatData we’ll need to write this out to a file so we’ll inject this in to our Begin script block along with our class definition to make this a stand alone script.

function Get-MyUsers ([string[]] $User)
{
    Begin
    {
        try{
            add-type @'
namespace JRICH
{
    public class Contact
    {
public string FName;
public string LName;
public string DisplayName()
        {
            return (this.FName + " " + this.LName);
        }
    }
}
'@
            }
        catch{}

$formatfile = "$pshome\jrich.contact.format.ps1xml"
$typedata = @'
<?xml version="1.0" encoding="utf-16"?>
<Configuration><ViewDefinitions><View>
    <Name>jrich.contact</Name>
    <ViewSelectedBy>
        <TypeName>jrich.contact</TypeName>
    </ViewSelectedBy>
    <TableControl>
        <TableHeaders>
            <TableColumnHeader><Label>DisplayName</Label></TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
                    <TableColumnItem><ScriptBlock>$_.displayname()</ScriptBlock></TableColumnItem>
                </TableColumnItems>
            </TableRowEntry>
        </TableRowEntries>
    </TableControl>
</View></ViewDefinitions></Configuration
'@

if(!(Test-Path $formatfile))
        {
$typedata | out-file $formatfile
        }
        Update-FormatData -AppendPath $formatfile
        $results = @()
    }
    Process
    {
foreach($usr in $user)
        {
            $tmp = new-object jrich.contact
            $tmp.lname, $tmp.fname = $usr.split(',')
$results += $tmp
        }
    }
    End
    {
        $results
    }
}

There are a couple of things to note here. First is that we’ve enclosed our type definition in a Try/Catch statement. The reason for this is that once we load our type in to the environment we cant do it again as we saw with creating it and trying to update it.

The next thing is the actual process of adding in the formating data. You’ll notice I define a file based on the $PSHome variable (PowerShell install location) and tacked on a file name. To make sure it’s never overwritten I’ve named it based on the namespace and class. We then define the XML as a string and check to see if that file is there and if not write the file and update the format data.

One thing to note is that this method should only be used when you want to make a single script file to be distributed. If you are making a module the process of format files is a bit different. Want to know about that? Comment on this post and I’ll do a write-up on it.

Hope this helps.

Managing Remote (WSMAN) sessions with PowerShell


There was a post on the forums asking about how to remove a session if the max sessions (5) had been reached. You could use Get-Process to find and kill a WsmProvHost but killing things is never a good idea. There is a Get-WSManInstance but it’s not very friendly so I’ve wrapped that and Remove-WSManInstance in to two functions that are much more user-friendly. Just copy and past this in to a file and you can use Import-Modulein your profile to add these functions in to your environment.

<#
.Synopsis
Display WSMan Connection Info
.DESCRIPTION
This is a wrapper to Get-WSManInstance
.EXAMPLE
Get-RemotePSSession ServerABC
.EXAMPLE
$s = Get-RemotePSSession ServerABC
$s | Remove-RemotePSSession
#>
function Get-RemotePSSession
{
[CmdletBinding()]
    Param
    (
        # Computer to query.
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [string]
$ComputerName,

# use SSL
        [switch]
$UseSSL
    )

    Begin
    {
        try{
            Add-Type  @"
namespace JRICH{
public class PSSessionInfo
            {
                public string Owner;
public string ClientIP;
public string SessionTime;
public string IdleTime;
public string ShellID;
public string ConnectionURI;
public bool UseSSL=false;
            }}
"@
        }
        catch{}
        $results = @()

    }
    Process
    {

$port = if($usessl){5986}else{5985}
$URI = "http://$($computername):$port/wsman"
$sessions = Get-WSManInstance -ConnectionURI $URI shell -Enumerate

foreach($session in $sessions)
        {
            $obj = New-Object jrich.pssessioninfo
            $obj.owner = $session.owner
            $obj.clientip = $session.clientIp
$obj.sessiontime = [System.Xml.XmlConvert]::ToTimeSpan($session.shellRunTime).tostring()
$obj.idletime = [System.Xml.XmlConvert]::ToTimeSpan($session.shellInactivity).tostring()
            $obj.shellid = $session.shellid
            $obj.connectionuri = $uri
            $obj.UseSSL = $usessl
            $results += $obj
        }
    }
    End
    {
        $results
    }
}

<#
.Synopsis
Logoff remote WSMAN session
.DESCRIPTION
   This function will take a JRICH.PSSessionInfo object and disconnect it
.EXAMPLE
$s = Get-RemotePSSession ServerABC
$s | Remove-RemotePSSession
#>
function Remove-RemotePSSession
{
[CmdletBinding()]
    Param
    (
        # Session to be removed.
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [jrich.pssessioninfo[]]
        $Session

    )

    Process
    {
foreach($connection in $session)
        {
Remove-WSManInstance -ConnectionURI $connection.connectionuri shell @{ShellID=$connection.shellid}
        }
    }

}

Edit: Thanks Alex for catching the use of “CredSSP” vs “UseSSL” which is the common arg on PSSession cmdlets!

PowerShell – The Trouble With All That Power


Lately I’ve seen a lot more conversations on the forums about how much error checking you should do, how careful you should be, should you include aliases in your modules. Silly questions you may say.

This comes down to two problems.

The Problems

The Admin

Think of this, PowerShell is the only language that has an interactive environment that allows you to deal with objects. Ok so there might be some others out there, but they aren’t as popular (and I don’t use them!). Whats the big deal you ask? If you are at all like me you leave your console (ISE for me) open all the time and by all the time I mean it loads when the system is booted and stays open (my system is only rebooted for patches). I don’t have a good reason other than I like PowerShell at my finger tips. Still with me? Now chances are if you do this you also tend to work in PowerShell and store data here and there to variables to quickly test or work with something and lets say its a server you’re working with, do you do this too?


$s = "MyServer"

gwmi win32_Computersystem -co $s

gwmi win32_share -co $s

Whats wrong with this? I stored my server name in $s which is certainly easy for me to type as I work on the fly. Problem is, a few hours later when I start to do something different I reuse that $s for another server, or perhaps something else. It’s just a server name, no big deal right? What about $dt to store a datetime object? I do that a lot, or $dt to store a data table. Perhaps you see where this is going. As this shell stays open for multiple days I might overwrite a variable and then when I want to look at it again  I have a problem (that datatable with all the SQL data now contains a date, opps.) This is obviously a user error and I’ve trained myself to open tabs to deal with problems like this (I really don’t want to type long variable names)

Problem solved!

No, the problem is that more and more people are developing some really neat modules. I personally try to stay away from them because I tend to take what I do on the fly and turn it in to a script that will be run from a server, and I’m not going to install modules there. Even with that said I find that my module collection is growing.

Where am I going with this? As you install more modules, and create your own and start working more and more in PowerShell you’re likely to run in to the problem of variable/alias confusion. Even though its a ‘Best Practice’ to label your functions with meaningful names (good job Quest: Get-QADUser)  you don’t always see that in practice. On the plus side MS is cutting this off with the new Intillisense that PS3 has which will ask which one you want to use. But still, as the years go by and more and more cool stuff comes out this will start to be a rather large problem. This isn’t something that we needed to deal with back in the day, once you ran the script, all of those variables were gone upon completion. If you worked in *nix, you likely didn’t use the shell like that (plus its all text and not objects, no fun.)

On top of this, more modules are creating variables. Most of them are fairly verbose with their names, but not all. I often find myself trying to use $host to store a hostname. As the PowerShell environment grows we’ll need to be more and more careful about what variables we use.

Advice?  First read on.

The Developer

The developer problem isn’t a new one, but it’s probably new to a lot of you PowerShell guys who are admins and want to use PowerShell to automate things and like the interactive console idea. My background is a mix bag of admin and development. My admin experience is much stronger, but I know enough in both areas to understand this problem.

As you write code you’re first goal is to make it do what you want it to do, which is the fun part. Once you get the code working the way you want, you should ask yourself, what could break this and how can I stop/trap it?

If you spend any deal of time on this you are likely to become overwhelmed with just how many things can break your code. I once heard of the 80/20 rule (there are many versions) for programming.

20% logic (the work) and 80% error checking. If you think that’s a lot, think of this. Lets say you want to modify a file on a remote system you’ll first want to verify that the system is online with a test-connection because its quick. Then you’ll want to make sure you can access the file location (timeout is generally slower for this) and then you want to check if the file is there and if it is you want to make sure you can edit it.

There are two methods of thought, test first, or trap errors. I wont say one method is better than the other, I use both depending on whats faster/safer.

Where am I going with this?

To the Point – The Knowledge:

The problem I see is that people do one of two things, they over think it and get overwhelmed with how many things can go wrong, or they just say it works, I’m done!

There is some great documents out there on this, but this isn’t a programming class, it’s an adaptation for the admin who wants to write code. It comes down to two main things:

Know The Audience – I think this is a big one, who will use this code? What is their ability level? Is it a helpdesk team that wont be able to figure out what the PowerShell error means or is it technical group?  Is it to be deployed as a server job? Is it being sold or published to the community?

Know The Risk – What is the repercussion of this script failing? If it’s a server job it could be really bad. You might lose data over it. If it’s for a client you run the risk of making them unhappy and increase support costs. If it’s for the community then the project could fail. If it’s for you, well, you probably don’t care.

My point here is that there is a middle ground on how much error checking needs to be done. Most aren’t doing enough, and some are over thinking it. So the next time you write a script and think you are down with it, think about your audience and what will happen if this script fails.

Powershell: Working with strings – Data Manipulation


In my first post, PowerShell: Working with Strings I talked about the types of strings and how to make them. Now lets talk about how to manipulate that data.

What it comes down to is one of two types of string data. It’s either a single string, or a collection of strings. Get-Content returns a collection of strings as do many other cmdlets and most cmd programs (ipconfig)  so we are going to work with these two sets.


$str1 = "hello world!"

$str2 = "hello","world"

The $str2 is just a way to simulate what Get-Content would give us.

First, lets start by looking to see what we can do with a basic string:


$str1 | gm

You’ll notice you have a System.String object and a bunch of methods to work with. One thing to keep in mind about a String is that its immutable which basically means you can’t change the value of the variable, you’ll need to store it to a new variable with the new value you want. Lets try to change our basic string to upper case:


$str1.ToUpper()

$str1

You’ll notice when we just type $str1 its back to lower case (ie, immutable).  So if we wanted to keep that we’d need to store it some place, but lets keep it as it is.

Looking at my original string you’ll see its all lower case, so lets change that h to an H. There are a few ways we can do it, and since we only have one H in this string, we have even more options. Most of these are a bit over kill, but they allow me to show an assortment of methods.


#simple replace

$str1.replace("h","H")

#sub string grabs from index to end in this case

#we just slap an H in front, good if there are multiple h's

"H" +&nbsp;$str1.substring(1)

"H$($str1.substring(1))"

#now in to the over kill

$str1.Insert(0,"H").Remove(1,1)

$str1.Remove($str1.IndexOf("h"),1).Insert(0,"h")

There are a few neat things to note here, the first being tacking on to methods.

$object.method().method().method() and so on

Which would work something like this

(($object.method()).method()).method()

Too many parentheses to follow? The point is, as you tack them on the new method works on the result of the previous, in our case it’s always a string, but that could change depending on what you are doing.

Another thing is the nesting of methods which can be handy, but some times harder to follow, in a script, it usually worth while to add a few extra characters to make it readable.

As you noticed when you did the Get-Member (shorthand gm) on the string, there were a lot of methods and it had the definition for each but most were packed in and it was hard to read, so if you are curious you can go to MSDN or you can type a method in without the () like so:


$str1.substring

Doing so will spit out something like this:

OverloadDefinitions

——————-

string Substring(int startIndex)

string Substring(int startIndex, int length)

Which shows the different ways to call the method. The first item is the return type, in both cases here it’s a string. Then it shows the name of the method and its param options. We have two cases here, a StartIndex and a StartIndex and Length. In the first case we provided a starting point and it goes to the end of the string, in the next case we provide a starting point and a distance.


$str1.substring(6)

$str1.substring(6,5)



So far everything we've been doing is .NET methods from the String object but PowerShell has some neat ways to handle this as well.



$str1 -replace "h", "H"

This works just as well but there a two very important notes here the first being that the .NET methods are case-sensitive and PowerShell is not, try this:


$str1 -replace "E", "h"

$str1.replace("E","h")

In the first case the e was replaced and in the second it was not.

The other thing to keep in mind is that many of the PowerShell comparison operators use regular expressions rather than just simple character replacement as the .NET methods do. I’ll go in to RegEx and the different operators in my next post on strings.

Now, I mentioned two types of string data and really only talked about one. The reason I made mention of both is so that you don’t try to do these operations on the return of a get-content or on the output of a command app.

One Thing I would like to point out is the Length property on our objects $str1 and $str2.


$str1.length

$str2.length

$str2[0].length

You’ll notice that the return on $str2.lenth is 2, which is how many items are in the array. This may seem obvious now, but when working with PowerShell you may not always know ahead of time if it’s an array or not (Another post on arrays some day.)

If however you have data like that and would like to make it a single string, you can do so with Join. Again you have the .NET and PowerShell way to do this.


$str2 -join " "

[string]::join(" ",$str2)

You’re probably wondering where the [string]::join came from. That’s a static method on the String class that was not listed by the gm we did before. If you looked at the MSDN docs you’ll see some methods with an S next to them, indicating they are static, but, who wants to leave PowerShell?


gm -i&nbsp;$str1 -Static

Thankfully the Get-Member cmdlet has a Static param that will show us the static methods available to an object of that type! Have I mentioned before how much I love Get-Member?

There is a ton more that can be done with strings but, I hope this enough to allow you to dig in to it more on your own!

Until next time (RegEx!)