PowerShell Cookbook

Search

Categories

 

On this page

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: 222
This Year: 0
This Month: 0
This Week: 0
Comments: 536

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

Tuesday, July 12, 2005 4:35:11 PM (Pacific Daylight Time, UTC-07:00)
I think you have your System.Net.WebClient and your System.Net.HttpWebRequest code examples switched. A good post regardless. Thanks!
Toby
Sunday, August 07, 2005 3:41:11 AM (Pacific Daylight Time, UTC-07:00)
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!
Sunday, August 07, 2005 8:10:29 PM (Pacific Daylight Time, UTC-07:00)
Updated, Vivek. Thanks.
Saturday, June 09, 2007 4:51:23 AM (Pacific Daylight Time, UTC-07:00)
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! :)
Thursday, March 20, 2008 10:20:27 AM (Pacific Daylight Time, UTC-07:00)
Thanks Vivek + Lee, you guys saved me time on a user-agent problem I had!
Name
E-mail
Home page

Comment (Some html is allowed: b, blockquote@cite, em, i, strike, strong, sub, super, u)  

Enter the code shown (prevents robots):