Automating the world one-liner at a time…
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
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 ,
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
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.
#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.
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".