Create a Truly Advanced PowerShell Function

Doctor Scripto

  Summary: Learn how to create a true advanced function that interacts with the Windows PowerShell pipeline.  Microsoft Scripting Guy Ed Wilson here. Sean Kearney, Microsoft Windows PowerShell MVP, has been burning up the keyboard lately with ideas for guest blog posts. I hate to disappoint him by not sharing his hard work with you. (I am sincerely appreciative of Sean’s hard work and willingness to share). Today Sean has written about using advanced functions and the pipeline. Take it away Sean…   I played this weekend because a good friend mentioned that my output from a blog post was only returning a hash table (and a messy one at that). Thanks, Klaus. I sat down this weekend and learned something. You see, initially when I learned about advanced functions (as an IT pro just hacking about), I found that all I had to do to make it work was tack Function NameofFunction() at the top of a script, and put a pair of curly brackets { } near the top and bottom, and it worked. Well…really…it sort of worked because I wasn’t adding in some key pieces. So this weekend I wanted to really learn it right. Here’s a simple script. It’s stupid, it’s pointless, and it’s an example…

param [String]$FullName

$FooT=$FullName.split(“/”)
$FooU=$FullName.toUPPER()
$FooD=$FullName.substring(3,2)

Return $FooT,$FooU,$FooD All this script does is take the string parameter $FullName, do some crazy stuff to the value, and spit it out as three objects. It would have been run like this:

$HereisSomethingToPlayWith=”C:MyStuffShouldNotBeOnTheFloorBigMess.txt”

./MYSILLYSCRIPT.PS1 $HereisSomethingToPlayWith What I wanted to do first was to get this to return a single object. That is done by using New-Object and defining a custom object as shown here.

$StuffBack=NEW-OBJECT PSOBJECT -property @{This=”;That=”;SomethingElse=”} You can edit the individual parts of the object like this…

$StuffBack.This=$FooT
$StuffBack.That=$FooU
$StuffBack.Somethingelse=$FooD …which will allow us to do this:

param [String]$FullName

$StuffBack=NEW-OBJECT PSOBJECT -property @{This=”;That=”;SomethingElse=”}

$FooT=$FullName.split(“/”)
$FooU=$FullName.toUPPER()
$FooD=$FullName.substring(3,2)

$StuffBack.This=$FooT
$StuffBack.That=$FooU
$StuffBack.Somethingelse=$FooD

Return $StuffBack This will give us one object, which is a lot cleaner on the output and “pipeline friendlier.” Now the fun stuff. If we want to take this to the next level and make it into a proper advanced function as opposed to only a .ps1 file, of course we put the function part at the top; but also, as part of a parameter, we make some changes. First, place the function name at the top of the script and add a curly bracket like so { at the top, and add one like so } as the last line in the script. I also would like to have this function available outside of the script as shown here:

Function Global:GET-foo()
{ We need to tack on a [CmdLetBinding], which states that this function should act just like a cmdlet. We also need to specify if the variable can accept input from the pipeline. This is done by changing the original parameter. And we need to specify that the content is now accepting an array of information (whether it’s one line or 1,000 lines from the pipeline, it’s still an array). Now here’s something I’m going to do for fun—I’m going to tell this particular advanced function that it is only to accept information from the pipeline if there is a property called FullName. By the way, all of this information can be found in the Help in the Windows Powershell ISE by searching for “about_Functions_Advanced.” Or you can run GET-HELP about_Functions_Advanced. So we’re going to change this…

param [String]$FullName …to this:

[CmdletBinding()]
Param(
[parameter](Mandatory=$True,
$ValueFromPipeline=$True)
$ParameterSetName=’FullName’]
[string[]]$FullName
) Now here’s the fun bit. You have to create a block in your script called Process. What this block says is “Any lines coming down the pipeline, I process here, and here is the code we’re going to run against those.” In the case of our original script, we want to have that as the pipeline process, so place the word Process just before your main block of code. But there’s another piece to watch out for. When you’re working with any of your previous values, you need to remember that you’re now working with an array. So although the Process block is only reading one line at a time, it is now one line in an array. You’ll have to edit the code to reflect that. So our original block changes from this…

$FooT=$FullName.split(“/”)
$FooU=$FullName.toUPPER()
$FooD=$FullName.substring(3,2) …to this now:

$FooT=$FullName[0].split(“/”)
$FooU=$FullName[0].toUPPER()
$FooD=$FullName[0].substring(3,2) The end result, if we put all the pieces together, is shown here:

Function global:GET-foo()
{

[CmdletBinding()]
Param(
[parameter](Mandatory=$True,
$ValueFromPipeline=$True)
$ParameterSetName=’FullName’]
[string[]]$FullName
)

Process
{

#Pointless Process

$StuffBack=NEW-OBJECT PSOBJECT -property @{This=”;That=”;SomethingElse=”}

$FooT=$FullName[0].split(“/”)
$FooU=$FullName[0].toUPPER()
$FooD=$FullName[0].substring(3,2)

$StuffBack.This=$FooT
$StuffBack.That=$FooU
$StuffBack.Somethingelse=$FooD

Return $StuffBack

}

} Now when we run our script, we’ll get a new cmdlet called Get-Foo. Here’s the neat bit. Type Get-Help, and you’ll actually see some rudimentary Help about it. You can press Tab to autocomplete the command name (using Tab to autocomplete a command also works on parameters). Remember that we said to accept only properties from the pipeline called FullName? I did this intentionally for this example. If you run this…

GET-CHILDITEM | GET-FOO …it will pull down the FullName property from Get-ChildItem for each individual object, and then mess with it. Yes, the actual function is completely pointless and trivial. But hopefully, you can see the basics about creating an advanced function and manipulating data in the pipeline. Remember the Power of Shell is in you! Sean
The Energized Tech   Thank you, Sean, for a great article about creating an advanced function in Windows PowerShell. I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace. Ed Wilson, Microsoft Scripting Guy  

0 comments

Discussion is closed.

Feedback usabilla icon