Param statement, and new-object

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.]

5 Responses to “Param statement, and new-object”

  1. Toby writes:

    I think you have your System.Net.WebClient and your System.Net.HttpWebRequest code examples switched. A good post regardless. Thanks!

  2. vivek writes:

    dang. just updated my whole blogging system so the links have changed:

    http://www.viveksharma.com/writings/2005/08/06/msh-script-get-uri/

    is where the post really lives. sorry about that lee!

  3. Lee Holmes writes:

    Updated, Vivek. Thanks.

  4. Mike Schinkel writes:

    Lee:

    Mucho thanks for this article, it really helped. However, I found an error that took me forever to diagnose, partly because I assumed your code worked and that it was me and not your code! đŸ™‚

    In your get-uri.msh for "the System.Net.HttpWebRequest way" you have the first two lines as:

    $request = [System.Net.WebRequest]::Create($uri)
    $request.Headers.Add("user-agent", $userAgent)

    First problem is that you used [System.Net.WebRequest] instead of [System.Net.HttpWebRequest]. Without setting the User-Agent it works with just WebRequest, but you have to use HttpWebRequest to set the User-Agent header.

    The second problem is that you can’t set the user-agent directly, you have to set it using:

    $request.UserAgent= $userAgent

    Now it is possible that your code worked with Monad Beta and not with PowerShell v1.0. Whatever the case I wanted to let others know so they didn’t have to spend two hours trying to figure it out like I did. (But don’t take that as criticism; without your article it would probably have taken me all day! đŸ™‚

  5. Max writes:

    Thanks Vivek + Lee, you guys saved me time on a user-agent problem I had!

Leave a Reply