PowerShell Cookbook

Twitter Updates

    follow me on Twitter

    Search

    Categories

     

    On this page

    Param statement, and new-object

    Archive

    Blogroll

    Disclaimer
    I work for Microsoft.

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    RSS 2.0 | Atom 1.0 | CDF

    Send mail to the author(s) E-mail

    Total Posts: 235
    This Year: 12
    This Month: 0
    This Week: 0
    Comments: 634

    Sign In

     Monday, July 11, 2005
    Tuesday, July 12, 2005 5:12:11 AM (Pacific Daylight Time, UTC-07:00) ( )

    Vivek recently posted a helpful script to get the contents of a web page.

     

    As is common with .Net and Monad, there is more than one way to do it.  In addition, there are also two Monad facilities we can take advantage of to make the script more readable: new-object, and the param statement.

     

    First of all, the easy one.  The new-object cmdlet creates a new object.  In the current drop, it only creates .Net objects, but COM support is coming soon.  This cmdlet replaces the calls to [Activator]::CreateInstance in Vivek's script.

     

    Next, the param statement.  I used it in my new-calendarItem script, and it looks like this:

     

    ## test-params.msh

    ## Test the param facility

     

    param(

       [string] $importantParameter = $(throw "Please specify the important parameter!"),

       [int] $countOfThings,

       [DateTime] $recordDate = [DateTime]::MaxValue

       )

      

    "ImportantParameter was $importantParameter"

    "CountOfThings was $countOfThings"

    "RecordDate was $recordDate"

     

    It looks similar to a function definition in many languages, but offers more benefits.  The param statement allows you to define the parameters to your script.  In the simplest case, your user supplies values for all of your parameters.  When they do this, Monad interprets them positionally for you.  That is, each parameter gets assigned from the value passed at that position on the command line.  For example:

     

    MSH:130 C:\Temp >test-params First 123 "Tuesday, July 12, 2005"

    ImportantParameter was First

    CountOfThings was 123

    RecordDate was 7/12/2005 12:00:00 AM

     

    Notice how Monad coerces the data types, if it can -- as with the DateTime value in the third position.

     

    In your script, you simply refer to your parameters by the names you gave them: $importantParameter, $countOfThings, and $recordDate.

     

    For some parameters, you might want to supply your user with sensible defaults.  In that case, Monad allows you to specify a default value for your parameter, as with $recordDate above:

     

    MSH:135 C:\Temp >test-params First 123

    ImportantParameter was First

    CountOfThings was 123

    RecordDate was 12/31/9999 11:59:59 PM

     

    However, your users don't always know how to interact with your script.  You might have parameters that should always be populated, such as $importantParameter in the example above.  In that case, we specify a special default value for that parameter -- an exception that tells them how to resolve the problem:

     

    MSH:136 C:\Temp >test-params

     : Please specify the important parameter!

    At d:\lee\tools\test-params.msh:5 char:42

    +    [string] $importantParameter = $(throw  <<<< "Please specify the important parameter!"),

     

    The param statement also gives you support of named parameters for free.  Named parameters let your user specify the parameters by name, rather than position.  Your parameter's variable name becomes the parameter name:

     

    MSH:141 C:\Temp >test-params -RecordDate:$([DateTime]::MinValue) `

    >>    -ImportantParameter:"Important!" `

    >>    -CountOfThings:123

    >> 

    ImportantParameter was Important!

    CountOfThings was 123

    RecordDate was 1/1/0001 12:00:00 AM

     

    As with regular cmdlets, you need only to specify enough of the parameter to differentiate it from the others:

     

    MSH:152 C:\Temp >test-params -r:$([DateTime]::MinValue) -i:"Important!" -c:10

    ImportantParameter was Important!

    CountOfThings was 10

    RecordDate was 1/1/0001 12:00:00 AM

     

    This also helps Monad disambiguate between multiple optional parameters:

     

    MSH:153 C:\Temp >test-params "Important!" -r:$([DateTime]::MinValue)

    ImportantParameter was Important!

    CountOfThings was 0

    RecordDate was 1/1/0001 12:00:00 AM

     

    MSH:154 C:\Temp >test-params "Important!" -c:123

    ImportantParameter was Important!

    CountOfThings was 123

    RecordDate was 12/31/9999 11:59:59 PM

     

    So, given this new-found knowledge, this is how we might rewrite Vivek's example.  First, the System.Net.WebClient way, as newly implemented in V2 of the framework:

     

    ## get-uri.msh

    ## Get a web page

     

    param(

       [string] $uri = $(throw "Please specify an URI to retieve"),

       [string] $userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"

       )

     

    $wc = new-object System.Net.WebClient

    $wc.Headers.Add("user-agent", $userAgent)

    $wc.DownloadString($uri)

     

    and the System.Net.HttpWebRequest way:

     

    ## get-uri.msh

    ## Get a web page

     

    param(

       [string] $uri = $(throw "Please specify an URI to retieve"),

       [string] $userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"

       )

     

    $request = [System.Net.WebRequest]::Create($uri)

    $request.Headers.Add("user-agent", $userAgent)

    $response = $request.GetResponse()

    $requestStream = $response.GetResponseStream()

    $readStream = new-object System.IO.StreamReader $requestStream

    $readStream.ReadToEnd()

    $readStream.Close()

    $response.Close()

     

    If we need the content split into an array (as with Vivek's script,) we can write:

     

    MSH:158 C:\Temp >$lines = (get-uri http://www.microsoft.com).Split("`n")

    MSH:159 C:\Temp >$lines[0]

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >

     

    For the purpose we need it, I much prefer the System.Net.WebClient way.

     

    [Edit: Thanks, Toby, for pointing out the WebClient / HttpWebRequest switch]

    [Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]