Advanced Functions and Test-LeapYear.ps1

Advanced Functions and Test-LeapYear.ps1

  • Comments 13

Welcome to CTP3!  There are just an amazing amount of really important features in V2 but the one I want everyone to start using ASAP is Advanced Functions.  In CTP2 we called this cmdlets.  We dropped that term in favor of taking the idea of a FUNCTION and just making it more and more capable.  It is a bit of a misnomer in that you can have an "Advanced function" by just adding a single line to your script.  You'll be amazed what that single line gives you; but we'll explore that later.  This entry recaps where we are with functions and then begins to give you a feel for some of the great benefits you have with advanced functions.  There is a LOT more to advanced functions than I'll show you here.  You get an incredible amount for just a tiny amount of additional code.

Going forward, I think that the community should/will establish the norm that the functions you share with other people and blog about should be Advanced Functions.

This post is a bit of mouthful but I encourage you to follow it and get on the Advanced Function bandwagon.  This is a gamechanger.  Alternatively, you can just start messing with them.  I've attached a ZIP file containing both Test-LeapYear.ps1 and a demo-test-leapyear.txt file that you can run using my Start-Demo script.   Either way - invest and you will be rewarded!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

 

[0]PS> #Functions can be simple
[1]PS> function Test-LeapYear {[DateTime]::IsLeapYear($args[0])}

[2]PS> Test-LeapYear 2008
True

 

[3]PS> # But there are issues with simplicity
[4]PS> # The obvious one is that you have readability issues.
[5]PS> # What is $args[0] anyway?
[6]PS> # Also you have no type checking on the parameter.

[7]PS> Test-LeapYear ThisYear
Start-Demo : Cannot convert argument "0", with value: "ThisYear", for "IsLe
apYear" to type "System.Int32": "Cannot convert value "ThisYear" to type "S
ystem.Int32". Error: "Input string was not in a correct format.""
At line:1 char:11
+ start-demo <<<<  .\demo-Test-LeapYear.ps1
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorE
   xception
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExce
   ption,Start-Demo

[8]PS> # There is no checking on the # of arguments
[9]PS> Test-LeapYear 2008 2009 2010 2011
True

 

[10]PS> ##############################################
[11]PS> # In V1 you could remedy these issues with Named parameters:

[12]PS> function Test-LeapYear {param([Int]$Year=[DateTime]::Now.Year)  [Dat
eTime]::IsLeapYear($Year)}

 

[13]PS> # Now you use variables whose names are self-documenting
[14]PS> # You get the same functions you got before

[15]PS> Test-LeapYear 2008
True

 

[16]PS> # But now you also get NAMED parameters which makes it
[17]PS> # even more self-documenting

[18]PS> Test-LeapYear -Year 2008
True

 

[19]PS> # You now have type checking on the parameter.
[20]PS> Test-LeapYear ThisYear
Start-Demo : Cannot process argument transformation on parameter 'Year'. Ca
nnot convert value "ThisYear" to type "System.Int32". Error: "Input string
was not in a correct format."
At line:1 char:11
+ start-demo <<<<  .\demo-Test-LeapYear.ps1
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorE
   xception
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExce
   ption,Start-Demo

 

[21]PS> # But there is still no checking on the # of arguments
[22]PS> Test-LeapYear 2008 2009 2010 2011
True

[23]PS> # Of course you were on your own for help as well.

 

 

 

[24]PS> ##########################################
[25]PS> #
[26]PS> # But now you have Advanced Functions:
[27]PS> Get-Content .\Test-LeapYear.ps1
<#
.Synopsis
    Determines whether a year is a leapyear or not.
.Description
    This is a simple function to demonstrate the capabilities of PowerShell
    V2 Advanced Functions by telling whether a year is a leap year or not.
.Parameter Year
    Which year would you like tested?
.Example
    PS> Test-LeapYear 2008

.ReturnValue
    [Boolean]

.Link
    about_functions
    about_functions_advanced
    about_functions_advanced_methods
    about_functions_advanced_parameters

.Notes
NAME:      Verb-Noun
AUTHOR:    NTDEV\jsnover
LASTEDIT:  12/22/2008 20:39:07
#Requires -Version 2.0
#>

param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
[Int]$Year
)

Process
{
    [DateTime]::IsLeapYear($Year)
}#Process

 

 

[28]PS> # You have all the stuff you had before but now look what you get:
[29]PS> # It ensures that you send it the right number of parameters:
[30]PS> .\Test-LeapYear 2008 2009 2010 2011
Start-Demo : A positional parameter cannot be found that accepts argument '
2009'.
At line:1 char:11
+ start-demo <<<<  .\demo-Test-LeapYear.ps1
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorE
   xception
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExce
   ption,Start-Demo

 

[31]PS> # It supports pipelining
[32]PS> 2008,2009,2010,2011 | .\Test-LeapYear
True
False
False
False

 

[33]PS> # I can't demo it year but it also supports Tab-Completion of paramete
rnames
[34]PS> # Best of all, it supports HELP
[35]PS> Get-Help .\Test-LeapYear -full

NAME
    C:\temp\Test-LeapYear.ps1

SYNOPSIS
    Determines whether a year is a leapyear or not.

SYNTAX
    C:\temp\Test-LeapYear.ps1 [-Year] [<Int32>] [-Verbose] [-Debug] [-Error
    Action [<ActionPreference>]] [-WarningAction [<ActionPreference>]] [-Er
    rorVariable [<String>]] [-WarningVariable [<String>]] [-OutVariable [<S
    tring>]] [-OutBuffer [<Int32>]] [<CommonParameters>]

DETAILED DESCRIPTION
    This is a simple function to demonstrate the capabilities of PowerShell
    V2 Advanced Functions by telling whether a year is a leap year or not.

PARAMETERS
    -Year
        Which year would you like tested?

        Required?                    true
        Position?                    0
        Default value
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?

    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, -WarningAction, -WarningVariable,
        -OutBuffer and -OutVariable. For more information, type,
        "get-help about_commonparameters".

INPUT TYPE

RETURN TYPE
    [Boolean]

NOTES

        NAME:      Verb-Noun
        AUTHOR:    NTDEV\jsnover
        LASTEDIT:  12/22/2008 20:39:07
        #Requires -Version 2.0

    -------------------------- EXAMPLE 1 --------------------------

    PS> Test-LeapYear 2008

RELATED LINKS
    about_functions
    about_functions_advanced
    about_functions_advanced_methods
    about_functions_advanced_parameters

 

[36]PS> # Notice that while we only declared one parameter,
[37]PS> # it actually has 9!
[38]PS> # These are provided by the PowerShell engine.
[39]PS> (Get-Command .\Test-LeapYear.ps1).Parameters

Key                                   Value
---                                   -----
Year                                  System.Management.Automation.Param...
Verbose                               System.Management.Automation.Param...
Debug                                 System.Management.Automation.Param...
ErrorAction                           System.Management.Automation.Param...
WarningAction                         System.Management.Automation.Param...
ErrorVariable                         System.Management.Automation.Param...
WarningVariable                       System.Management.Automation.Param...
OutVariable                           System.Management.Automation.Param...
OutBuffer                             System.Management.Automation.Param...

 

[40]PS> # Let's see them in action
[41]PS> 2008,2009,2010 |.\Test-LeapYear.ps1 -OutVariable Results
True
False
False

[42]PS> $results
True
False
False

 

 

[43]PS> 2008,"Burp",2010 |.\Test-LeapYear.ps1 -ErrorAction Stop
True
Start-Demo : Command execution stopped because the preference variable "Err
orActionPreference" or common parameter is set to Stop: The input object ca
nnot be bound to any parameters for the command either because the command
does not take pipeline input or the input and its properties do not match a
ny of the parameters that take pipeline input.
At line:1 char:11
+ start-demo <<<<  .\demo-Test-LeapYear.ps1
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorE
   xception
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExce
   ption,Start-Demo

[44]PS> 2008,"Burp",2010 |.\Test-LeapYear.ps1 -ErrorAction SilentlyContinue

True 
 False

Attachment: Test-LeapYear.zip
Leave a Comment
  • Please add 3 and 4 and type the answer here:
  • Post
  • One of my favorite parts of Cmdlets/Advanced Functions is the ParameterSets.  These truly give a customized experience to the end user depending on what parameters they pass.  I highly encourage people to explore those options as well.

    -Shane

  • 2008 年もあと少し。 今年も皆さんにはお世話になりました。 私事ですが来る新年も祖母が他界したため年始のご挨拶を控えさせて頂きます。 今後ともよろしくどうぞ...

  • Mr. Snover ..

    This is some awesome stuff Sir . Thank you for CTP 3 . Happy Holidays !!

    Chris

  • Thanx !

    A Happy Christmas to the Team.

  • I'm unclear on the use of the "<#" and "#>" lines.

    When I use them in my own scripts, they do work (no help info is produced. Removing them and the get-help returns help info.

    Also - putting in a #Requires -version 2.0 stops help from working.

    Is there some documentation on this feature?

  • Why must the parameter position be specified?

    Isn't the order of parameter definition an indicator for position and sufficient?

    What is the use case for having the position specifier?

  • @Tfl

    "<#" and "#>" are block comment characters.  Everything between them is a comment.

    Please file a bug on the #Requires breaking help.

    jps

  • @alex

    Great point!

    The problem comes when you have multiple parametersets.  That might be hard to have an obvious solution.  That said, the whole point of making these FUNCTIONS was to provide a smooth glide path from simple to sophosticated.  As such, we could do this for the vast majority of the functions which will only have a single parameterset.

    Please file this as a bug/suggestion (on https://connect.microsoft.com)  - we are at a point where that is the only input we can consider acting on for this release.

    Thanks!

    jps

  • JPS ,

    I have a question why did you not start the function with Function Test-Leapyear {

    }

    I just noticed this .Is this because your using a process block ?

    I had created a similar function and encased it in a Function Get-whatever {} with the process block inside and it didn't work . Taking away the Function keyword like you have written it works fine . Just wanted to know why that was ?

    Chris

  • @Chris

    I put it into a file Test-Leapyear.ps1 and put it into my path.  That way I can just type Test-Leapyear and PS will find it and invoke it.  When you declare it a FUNCTION you have to DOT-SOURCE the file before you can invoke it.  There are pros and cons to each approach.  This wasn't a function I intend to use a lot and maximal performance doesn't matter so I decided to make it an external file.

    jps

  • #Requires -Version 2.0

    Is this supported in V1.0 ie is there a way to get PS V1.0 throw an error if it runs a scritpt that needs V2.0 of PS?

  • > Is this supported in V1.0 ie is there a way to get PS V1.0 throw an error if it runs a scritpt that needs V2.0 of PS?

    Absolutely.

    jps

  • It seams as if the ".ReturnValue" in the help is not valid any longer. At least for me i cant get the help to work with ".ReturnValue".

Page 1 of 1 (13 items)