Monthly Archive

Categories

Monthly Archives: April 2010

Scripting Games – very very soon

The Scripting Games are almost upon us but this year there is a slight change for me.  I’m not going to be taking part.

Oh no you lucky people I’m going to be a judge - he he he – even more of an insane cackle than usual.

I will expect a superior standard in the PowerShell scripts submitted otherwise there will be extra work after school.

 

Seriously – good luck to everyone planning to enter the games and if you haven't registered yet skip over to http://2010sg.poshcode.org/ and register now.

Summing and counting

One problem that seems to keep arising is counting the number of records in a file and summing the contents of one or more files.  If we start with a couple of files:

test.txt

Green,123.45
Brown,456.77
Black,890.12
White,679.43

and test.csv

Name,Amount
Green,123.45
Brown,456.77
Black,890.12
White,679.43

The only difference is that the csv file has a header row.

If we start with the csv file we can do this

PS> Import-Csv c:\test\test.csv | Measure-Object -Property Amount -Sum

Count    : 4
Average  :
Sum      : 2149.77
Maximum  :
Minimum  :
Property : Amount

Nice and easy – just the way we like.  Working with the text file may be a bit more difficult as we can’t use Measure-Object directly. The traditional approach would be

001
002
003
004
005
006
007
008
009
010
$count = 0
$amount = 0.00

Get-Content c:\test\test.txt |
foreach {
    $data = $_ -split ","
    $count++   
    $amount += $data[1]
}
Write-Host "$count records and $amount in total"

 

Set a couple of variables – one as a counter and one to hold the sum.  Use Get-Content to read the file on to the pipeline. We then use foreach to sum the amounts. Our final action is to write out the answers.

There is a better way. In PowerShell v2 we get some extra parameters that enable us to add a header to the file before we add it to the pipeline.

PS> Import-Csv c:\test\test.txt -Header "Name","Amount" | Measure-Object -Property Amount -Sum

Count    : 4
Average  :
Sum      : 2149.77
Maximum  :
Minimum  :
Property : Amount

If we change the delimiter to something other than a comma e.g. a semi-colon

Green;123.45
Brown;456.77
Black;890.12
White;679.43

We can still add the header.  We just need to define the delimiter. Check the import-csv help file for full details.

PS> Import-Csv c:\test\test.txt -Header "Name","Amount" -Delimiter ";" | Measure-Object -Property Amount -Sum

Count    : 4
Average  :
Sum      : 2149.77
Maximum  :
Minimum  :
Property : Amount

Just because a file doesn’t have a header doesn’t mean we can’t work with it as a csv!

Technorati Tags: PowerShell,csv file

WMI posts

Couple of WMI based posts you may be interested in:

 

Finding the current logged on user

http://itknowledgeexchange.techtarget.com/powershell/current-logged-on-user/

 

and the Windows System Assessment Tool

http://itknowledgeexchange.techtarget.com/powershell/windows-system-assessment-tool/

Technorati Tags: PowerShell,WMI

You are unique

Lets start with some data

$data =@(1,1,1,1,12,23,4,5,4643,5,3,3,3,3,3,3,37,7,7,8,9,9,0,0,12)

We now want to select the unique values out of this list

if I’ve wanted only the unique values I’ve tended to do this

$data | select –Unique

 

there are other options. If you want the output sorted you may be tempted by

$data | sort | select –Unique

 

but you can do this instead

$data | sort –Unique

 

or even

$data | sort -Unique –Descending

 

A final option is

$data | sort | Get-Unique

 

The input to Get-Unique must be sorted – it isn’t important to the other two methods

More info in the help files.  Looks like PowerShell doesn’t have a unique method of getting unique values

Technorati Tags: PowerShell,uniqueness

Scripting Games start soon

This years Scripting Games start next week – Monday 26 April 2010 – see

http://blogs.technet.com/heyscriptingguy/archive/2010/04/05/2010-scripting-games-all-links-on-one-page.aspx

PowerShell UG Recording April 20

Thanks to everyone who came on to the Live Meeting tonight.  For anyone who missed it:

The slides and demo are available on my sky drive at

http://cid-43cfa46a74cf3e96.skydrive.live.com/browse.aspx/PowerShell%20User%20Group/April%202010

The recording of the meeting is available from

Richard Siddaway has invited you to view a Microsoft Office Live Meeting recording.
View Recording
Recording Details
    Subject: PowerShell Modules
    Recording URL: https://www.livemeeting.com/cc/usergroups/view
    Recording ID: B3FG29
    Attendee Key: Zft%D4B7H

This Office Live Meeting invitation is a personal invitation meant only for you. It should not be forwarded. If you received this email by mistake or require Live Meeting Assistance, please refer to the Live Meeting Assistance Center at http://r.office.microsoft.com/r/rlidLiveMeeting?p1=12&p2=en_US&p3=LMInfo&p4=support

 

Next meeting is 18 May when we look at PowerShell eventing

Technorati Tags: PowerShell,User Group

PowerShell on Bing

The PowerShell team blogged about a new visual search capability for Powershell help care of Bing

http://blogs.msdn.com/powershell/archive/2010/04/19/windows-powershell-in-bing-visual-search.aspx

Couple of points

- make sure that you are looking at the US version of Bing – it doesn’t show in others (at least it doesn’t in the UK version)

  - click on the country name at top right on the bing welcome page and select United States – English

- select Visual Search

- select Reference

- scroll down for PowerShell – under the Scripting heading

 

Only the core PowerShell cmdlets are available – hopefully other teams will follow suit.

Technorati Tags: PowerShell,bing,help

UG Reminder

The PowerShell UG Live Meeting is tomorrow night (20 April) at 7.30pm BST.  Subject is modules.

Details from http://msmvps.com/blogs/richardsiddaway/archive/2010/04/12/powershell-user-group-live-meeting-modules.aspx

Technorati Tags: PowerShell,User group,Modules

Advanced Functions: part 3

Lets start looking at validation of parameter values. We have a few options to try

  [ValidateCount(1,n)] 
  [ValidateLength(1,m)] 
  [ValidatePattern("[A-Z][A-Z]A-Z][0-9]")] 
  [ValidateRange(0,10)] 
  [ValidateScript({$_ -lt 4})] 
  [ValidateSet("red","blue","green")] 

  [ValidateNotNull()] 
  [ValidateNotNullOrEmpty()]

 

lets start with validating a range of values

001
002
003
004
005
006
007
008
function test1 {
    param (
        [int]
        [validateRange(1,5)]
        $p1  
)
    $p1 * 3   
}

We have a simple function that defines 1 parameter – in my recent post showing all of the possible parameters [datatype] referred to the data type we were assigning to the parameter in this case an integer.

We’ll define a valid range of 1-5 for this value

Our function does a simple multiplication to show its working.

As a test

PS> 1..5 | foreach {test1 $_}
3
6
9
12
15

Everything works.  what happens if we submit a value outside the allowed range?

PS> 4..7 | foreach {test1 $_}
12
15
test1 : Cannot validate argument on parameter 'p1'. The 6 argument is greater than the m
aximum allowed range of 5. Supply an argument that is less than 5 and then try the comma
nd again.
At line:1 char:22
+ 4..7 | foreach {test1 <<<<  $_}
    + CategoryInfo          : InvalidData: (:) [test1], ParameterBindingValidationExcep
   tion
    + FullyQualifiedErrorId : ParameterArgumentValidationError,test1
test1 : Cannot validate argument on parameter 'p1'. The 7 argument is greater than the m
aximum allowed range of 5. Supply an argument that is less than 5 and then try the comma
nd again.
At line:1 char:22
+ 4..7 | foreach {test1 <<<<  $_}
    + CategoryInfo          : InvalidData: (:) [test1], ParameterBindingValidationExcep
   tion
    + FullyQualifiedErrorId : ParameterArgumentValidationError,test1

 

4 and 5 are accepted but 6 and 7 are rejected.

If we fall outside the lower range

PS> 2..0 | foreach {test1 $_}
6
3
test1 : Cannot validate argument on parameter 'p1'. The 0 argument is less than the mini
mum allowed range of 1. Supply an argument that is greater than 1 and then try the comma
nd again.
At line:1 char:22
+ 2..0 | foreach {test1 <<<<  $_}
    + CategoryInfo          : InvalidData: (:) [test1], ParameterBindingValidationExcep
   tion
    + FullyQualifiedErrorId : ParameterArgumentValidationError,test1

A simple test that protects the function. Using this supposes we know what the range of values should be

IE History

Saw a post on the forums that looked interesting.

Problem

Convert  this VBScript into PowerShell

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
HISTORY_LIST = 34
ITEM_NAME = 0
ITEM_DATE = 2
Set objShell = CreateObject("Shell.Application")
Set objHistory = objShell.NameSpace(HISTORY_LIST)
Set objHistoryFolder = objHistory.Self
Wscript.Echo vbCrLf & "Location of History"
Wscript.Echo objHistoryFolder.Path

For Each objPeriod In objHistory.Items

  Wscript.Echo vbCrLf & objPeriod.Name
  Wscript.Echo String(Len(objPeriod.Name), "=")

 If objPeriod.IsFolder Then
    Set objSiteFolder = objPeriod.GetFolder

    For Each objSite In objSiteFolder.Items
      Wscript.Echo vbCrLf & objSite.Name
      Wscript.Echo String(Len(objSite.Name), "-")

      If objSite.IsFolder Then
        Set objPageFolder = objSite.GetFolder

        For Each objPage In objPageFolder.Items
          strURL = objPageFolder.GetDetailsOf(objPage,ITEM_NAME)
          WScript.Echo vbCrLf & "URL: " & strURL
          strDateVisited = objPageFolder.GetDetailsOf(objPage,ITEM_DATE)
          WScript.Echo "Date Visited: " & strDateVisited
        Next

      End If

   Next

  End If
Next

 

The script came from http://gallery.technet.microsoft.com/ScriptCenter/en-us/28696fd5-2a90-4766-9b96-d4bc38c9db12 and interestingly was only stated to work on Windows 2000 and Windows XP

For my first go I did a straight conversion

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
$shell = New-Object -ComObject Shell.Application
$hist = $shell.NameSpace(34)
$folder = $hist.Self
$folder.Path

$periods = $hist.Items()
foreach ($period in $periods){
    ""
    ""
    $period.Name
   
    if ($period.IsFolder) {
        $siteFolder = $period.GetFolder
        $sites = $siteFolder.Items()
       
        foreach ($site in $sites) {
            ""
            $site.Name
           
            if ($site.IsFolder) {
                $pageFolder  = $site.GetFolder
                $pages = $pageFolder.Items()
               
                foreach ($page in $pages) {
                    $url = $pageFolder.GetDetailsOf($page,0)
                    $date =  $pageFolder.GetDetailsOf($page,2)
                    "$date visited $url" 
                }
            }
        }
    } 
}

 

We start off the same in creating a COM object and access special folder 34 – the IE history.

After displaying the folder path we get the periods and iterate through them, displaying the name – this will be the day of the week or Last week etc

We check if the period is a folder and if it is we get the items in the folder.

We can then iterate through them displaying the site name and checking if it is a folder.

if it is we can get the pages visited and display the URL and the date visited.

While this worked I was 100% happy with it because of the nested foreachs.  I wanted to do more on the pipeline so I had another go

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
$shell = New-Object -ComObject Shell.Application
$hist = $shell.NameSpace(34)
$folder = $hist.Self
$folder.Path

$hist.Items() | foreach {
    ""; ""; $_.Name
     if ($_.IsFolder) {
         $siteFolder = $_.GetFolder
         $siteFolder.Items() | foreach {
            $site = $_
            ""; $site.Name
            if ($site.IsFolder) {
                $pageFolder  = $site.GetFolder
                $pageFolder.Items() | foreach {
                    $url = $pageFolder.GetDetailsOf($_,0)
                    $date =  $pageFolder.GetDetailsOf($_,2)
                    "$date visited $url" 
                }
            }
         }
     }
}

 

This does exactly the same as the first attempt. It could be made more compact but is already getting a bit hard to understand. For once the more verbose way may be better.

BTW I did this on Windows 7