/\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")
$PreviousItems = (" Moving to ThePowerShellGuy.com "," PowerShell Code formatting test for my new Blog "," PowerShell Code formatting test for my new Blog "," PowerShell Community Extensions V1.0 "," PowerShell : Access remote eventlogs "," Windows PowerShell Scripting Sweepstakes! "," PowerShell : making Custum Enums "," TechNet Webcast: An Overview of Windows PowerShell "," "Windows PowerShell: TFM" goes "Gold" "," PowerShell : Advanced renaming of files "," ")

Wednesday, May 31, 2006

 


Powershell links and free E-book



DontBotherMeWithSpam, started a PowerShell link collection on delicious, http://del.icio.us/powershell

next to the Powershell book previews already metioned (and on the delicious links), I noticed that I did not yet linked to this one yet :

In this free e-book on realtimepublishers.com about PowerShell you can find a good introduction to PowerShell. An Introduction to Microsoft PowerShell by Don Jones

and on the codeproject a nice article about writing a CMDlet to use Copernic Desktop Search (CDS) application from powershell.

http://www.codeproject.com/useritems/getfromcopernic.asp

Enjoy,

Greetings /\/\o\/\/
Tags :


 1 comments

Sunday, May 28, 2006

 


PowerShell Tab Completion



PowerShell Tab Completion is since RC1 customizable,

you can look at the current implementation like this :

get-content function:TabExpansion

This is my first customization to this function :

I added Tab-completion for Types to get the static methods (and properties)
I like this especialy becouse normaly you have to remember to use get-member - static to list them.

This also works for Enums :

[regex]::M [tab]
[regex]::Match(

[dateTime]:: [tab]
[datetime]::Compare(

[consolecolor]::B [tab]
[consolecolor]::Black [tab]
[consolecolor]::Blue [tab]
[consolecolor]::Black



the Code I added looks like this :


function tabexpansion {

# (Original Code Here )

            switch -regex ($lastWord)
            {

# this Part is added to handle Types
# /\/\o\/\/ 2006
      
               # Handle Types
               '(\[.*\])::(\w*)' {
                    invoke-expression "$($matches[1]) | gm -static" | where {$_.name -like "$($matches[2])*"} |% {
                      if ($_.MemberType -band $method) {
                        "$($matches[1])::$($_.name)" + '('
                      } Else {
                        "$($matches[1])::$($_.name)"
                      }
                    }
               }

# End of added Part

               # Handle property and method expansion...

# (Original Code Here )

}


*Note* if you output the original code to a file (gc function:TabExpansion | out-file) you will miss the line "Function {" and the last "}" you need to add it to declare the function again.

then below the Switch statement (I left in in for readability, you add the custom code.

you need to . source the script to overwrite the $global tabExpansion function.
and put it in your profile to use it every session.

Enjoy,

gr /\/\o\/\/
Tags :


posted by /\/\o\/\/
 4 comments

Friday, May 26, 2006

 


PowerShell out-DataGrid update and more Dataset utilities



As I renamed the out-PropertyGrid function entry,as I wrongly titled it as out-DataGrid

as Abhiskek did an out-DataGrid function here : Abhishek's PowerShell Blog
and I already had one for Monad this did lead to some confusion so I renamed it but the link has also changed, this is the new one powershell-out-propertygrid-msh-view.html

as I did not convert my out-DataGrid yet (changing MSH to PS in 2 places)
I will also provide my version here, the diffence is that I as a Datagrid and Abhiskek did use a array.
my version is a bit bigger, but I like the datatable as Greg did notice in the comments the sort in the Datagrid will not work on an array, but does an the datagrid and also that I can combine the data from all kind of sources :(see working with CSV files in MSH (part one) and some of the links or seach for system.data on my blog to find more info.)
and as you can see here : MSH GUI Viewer for Dataset Example in some screenshots, so you can walk the relations you did make (only the datagridpart not the converting part for the rest the same as for the old focus hack)

I also have a script using only the convert to dateTable, that I will also post ,
and the helpers to get a CSV file or a Excel sheet into a dataTable ( some already posted )

so now you have the toolset to get together changed for PowerShell , and you can just past in the whole bunch and get working with CSV file Excel files DataTables and Combining them, and last but not least viewing them in a GUI.

a last remark note that the functions are made for commandline usage,
so you dot-source the connect funtions (otherwise csv files or excel sheets are listed but go out of scope after running the function and con not be used) :
then the functions just assume and take the connection object ($con or $xlsCon) as i explained in former posts.

so you work like this :

# takes directory name as parameter CSV and TXT files in directory are tables.

. connect-csv c:\Csvfiles

# takes Excel filename as parameter Sheets are datatables 

. connect-Excel c:\ExcelFiles\file.xls

# after connecting you can just use the get-* functions 

# get CSV file 

$file = get-DataTable file#csv

# get XlsSheet (defaults to sheet1$ )

$file2 = get-xlsSheet 

# make new dataset

$ds = new-object System.Data.dataSet

# merge the files 

$ds.merge($file)
$ds.Merge($file2)

# show in GUI (no parameter just assumes there is a $ds object ;-) )

show-DataSet

#  out-dataTable will convert command output to a dataTable ( you can merge again in the $ds)

$dt = gps | out-dataTable 



and here are the updates and utility scripts :
(note most parts are formerly posted on my blog and origional entrys contain more information about the workings, so if things are not clear try the links)

# Function out-datagrid 
# shows piplineinput in a GUI using a datagrid 
# /\/\o\/\/ 2006 
# http:mow001.blogspot.com 

Function out-dataGrid { 

  # Make DataTable from Input 

  $dt = new-Object Data.datatable 
  $First = $true 
  foreach ($item in $input){ 
    $DR = $DT.NewRow() 
    $Item.PsObject.get_properties() | foreach { 
      If ($first) { 
        $Col =  new-object Data.DataColumn 
        $Col.ColumnName = $_.Name.ToString() 
        $DT.Columns.Add($Col)       } 
      if ($_.value -eq $null) { 
        $DR.Item($_.Name) = "[empty]" 
      } 
      ElseIf ($_.IsArray) { 
        $DR.Item($_.Name) =[string]::Join($_.value ,";"
      } 
      Else { 
        $DR.Item($_.Name) = $_.value 
      } 
    } 
    $DT.Rows.Add($DR) 
    $First = $false 
  } 

  # show Datatable in Form  

  $form = new-object Windows.Forms.form  
  $form.Size = new-object System.Drawing.Size @(1000,600)  
  $DG = new-object windows.forms.DataGrid 
  $DG.CaptionText = "Number of Rows $($DT.rows.Count)"  
  $DG.AllowSorting = $True  
  $DG.DataSource = $DT.psObject.baseobject  
  $DG.Dock = [System.Windows.Forms.DockStyle]::Fill  
  $form.text = "$($myinvocation.line)"  
  $form.Controls.Add($DG)  
  $Form.Add_Shown({$form.Activate()})  
  $form.showdialog()  


##########################################################
# Load CSV and Excel ADO helper functions

# connect to Excel File

function connect-Excel {
  Param ($Path = "")
  $ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$path;Extended Properties=Excel 8.0;" 
  $XlsConn = new-object System.Data.OleDb.OleDbConnection($connString)
  $Xlsconn.open()
  $Tables = $xlsConn.GetOleDbSchemaTable([System.Data.OleDb.OleDbSchemaGuid]::tables,$null)
  $Tables |% {$_.TABLE_NAME}
}

# get a sheet from excel into a datatable

function get-xlsSheet {
  Param ($name = "sheet1$")
  $cmd = new-object System.Data.OleDb.OleDbCommand("Select * from [$name]",$xlsConn)
  $da = new-object System.Data.OleDb.OleDbDataAdapter($cmd)
  $dt = new-object System.Data.dataTable
  [void]$da.fill($dt)
  $dt.TableName = $name
  return $dt
}

# connect to a directory with CSV files :

function connect-csv {
  Param ($Path = ".")
  $ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$path;Extended properties='text;HDR=Yes;FMT=Delimited'"
  $Conn = new-object System.Data.OleDb.OleDbConnection($connString)
  $conn.open()
  $Tables = $Conn.GetOleDbSchemaTable([System.Data.OleDb.OleDbSchemaGuid]::tables,$null)
  $Tables |% {$_.TABLE_NAME}
}

# get CSV / txt file into a dataTable 

function get-DataTable {
  Param ($name)
  $cmd = new-object System.Data.OleDb.OleDbCommand("Select * from [$name]",$Conn)
  $da = new-object System.Data.OleDb.OleDbDataAdapter($cmd)
  $dt = new-object System.Data.dataTable
  [void]$da.fill($dt)
  $dt.TableName = $name.split("#")[0]
  return $dt
}

# show the $ds dataSet in a GUI

Function show-DataSet {
  $form = new-object Windows.Forms.form  
  $form.Size = new-object System.Drawing.Size @(1000,600)  
  $DG = new-object windows.forms.DataGrid
  $DG.CaptionText = "Number of Rows $($Ds.rows.Count)"  
  $DG.AllowSorting = $True  
  $DG.DataSource = $Ds.PSobject.baseobject  
  $DG.Dock = [System.Windows.Forms.DockStyle]::Fill  
  $form.text = "$($myinvocation.line)"  
  $form.Controls.Add($DG)  
  $Form.Add_Shown({$form.Activate()})  
  $form.showdialog()  
}

# convert commandoutput to DataTable :

Function out-dataTable {

  # Make DataTable from Input

  $dt = new-object Data.datatable
  $First = $true
  foreach ($item in $input){
    $DR = $DT.NewRow()
    $Item.PsObject.get_properties() |% {
      If ($first) {
        $Col =  new-object Data.DataColumn
        $Col.ColumnName = $_.Name.ToString()
        $DT.Columns.Add($Col)       }
      if ($_.value -eq $null) {
        $DR.Item($_.Name) = "[empty]"
      }
      ElseIf ($_.IsArray) {
        $DR.Item($_.Name) =[string]::Join($_.value ,";")
      }
      Else {
        $DR.Item($_.Name) = $_.value
      }
    }
    $DT.Rows.Add($DR)
    $First = $false
  }
  return $dt
}


As most where mentioned before and not all had to be changed I still reposted them all as I hope this combination of scripts helps to see the complete picture of combining data in PowerShell using ADO (the samples are not complete as you can add Access and SQL also to the toolkit as you can see in other entries, but that a leave up to you to make the post not even bigger)

but I mean that you can just past all the code into your PowerShell console, pick a directory that contains CSV files ans an Excel sheet and follow the examples above and in the other posts and get experimenting.

also be sure the check the select samples in the CSV series to create new combined datasets and working with export-csv to safe them.
and follow the post about how to make relations and then use Show-DataSet again and see how you can walk the relations.

I use this a lot comparing information from different sources.

let me know what you think of this way to work with data in Powershell

Enjoy,

Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 0 comments

Thursday, May 25, 2006

 


PowerShell : Getting Subdirectories using WMI part 3



The shortcut to filtering subdirectories, I made in : PowerShell : Getting Subdirectories using WMI part 2 was not special character proof, e.g. "mow's dir" went wrong,

So here 2 other solutions using WMI relations :

# Get Only SubDirectories

(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated($null,"Win32_SubDirectory",$null,$null,$null,"GroupComponent",$false,$null)

# or

(new-object Management.ManagementObjectSearcher('associators of {Win32_Directory.Name="c:\\foobar"} where assocclass = Win32_SubDirectory role = GroupComponent')).get()


But how did I come to those ?

As I did say before I showed the wrong workaround in the first, the Management.RelatedObjectQuery helps with this task so following and using the 2 former posts :

Lets Query WMI from MSH

PowerShell out-DataGrid (MSH view-Object)

but as working with relations is an advanced WMI topic, and the former Service example used another kind of relation, I will show how I came to the query in this case.

I came to this using the commandline like this :

first get some info using the getrelationships() method :

# Kind of RelationShips

MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelationships() | fl __class


__class : Win32_SubDirectory

__class : Win32_SubDirectory

__class : Win32_SubDirectory

__class : Win32_SecuritySettingOfLogicalFile


# Roles :

MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelationShips('Win32_SubDirectory') | fl [a-z]*


GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar"
PartComponent  : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar\\bar"

GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar"
PartComponent  : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar\\Foo"

GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\"
PartComponent  : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar"


we already used the simple overloads using no filter or only the related class,
but the next step is that big overload :

# get available overloads :

MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated.OverloadDefinitions
System.Management.ManagementObjectCollection GetRelated()
System.Management.ManagementObjectCollection GetRelated(String relatedClass)
System.Management.ManagementObjectCollection GetRelated(String relatedClass, String relationshipClass, String relationshipQualifier, String
 relatedQualifier, String relatedRole, String thisRole, Boolean classDefinitionsOnly, EnumerationOptions options)


how to fill that big overload, we use the data we have from getrelationships :

we want only Win32_SubDirectory and we want only the ones where we are the GroupComponent.

so we use a RelatedObjectQuery to see how to do that :

MowPS>$roq = new-object System.Management.RelatedObjectQuery
MowPS>$roq


IsSchemaQuery         : False
SourceObject          :
RelatedClass          :
RelationshipClass     :
RelatedQualifier      :
RelationshipQualifier :
RelatedRole           :
ThisRole              :
ClassDefinitionsOnly  : False
QueryLanguage         : WQL
QueryString           :

# fill in the parts we know now :

$roq = new-object System.Management.RelatedObjectQuery
$roq.SourceObject = 'Win32_Directory.Name="c:\\foobar"'
$roq.RelationshipClass = 'Win32_SubDirectory'
$roq.ThisRole = 'GroupComponent'


# or use ProertyGrid to Edit :

$roq = new-object System.Management.RelatedObjectQuery
$roq | out-propertygrid

# Result

MowPS>$roq


IsSchemaQuery         : False
SourceObject          : Win32_Directory.Name="c:\\foobar"
RelatedClass          :
RelationshipClass     : Win32_SubDirectory
RelatedQualifier      :
RelationshipQualifier :
RelatedRole           :
ThisRole              : GroupComponent
ClassDefinitionsOnly  : False
QueryLanguage         : WQL
QueryString           : associators of {Win32_Directory.Name="c:\\foobar"where assocclass = Win32_SubDirectory role = GroupComponent

# Use with ManagementObjectSearcher

# Use Query Object :

$mos = new-object system.management.ManagementObjectSearcher($roq)
$mos.get()

# Or Query 

(new-object Management.ManagementObjectSearcher('associators of {Win32_Directory.Name="c:\\foobar"} where assocclass = Win32_SubDirectory role = GroupComponent')).get()


now that you can see parameters like this the overload in not that hard do,
just fill in what you know.

You see using the .NET system.management helper classes and out-propertyGrid can make this task a whole bit easier.
just copy the properties of $roq or just use it.

Enjoy,

Greetings /\/\o\/\/

Tags :


posted by /\/\o\/\/
 0 comments

Monday, May 22, 2006

 


PowerShell : Getting Subdirectories using WMI part 2



In the last post PowerShell : Getting Subdirectories using WMI ,
I did use the WMI relation to get a folders subfolder.

Pete did ask 3 questions in the comments of the last post.
I think they were interesing enough to make a new post,

first about using $name and just the string of the directoryname,

as I made commandline examples I did just type in the string, and did $name = 'c:\foo' as you would do that in the Share example (it would be the path of the share there, i give that example in the NG I think.)
I just mixed it up in the examples by acident sorry if that was confusing.

but for the second question how to handle the escapingm if you get back a path without the escaping you can use the replace method.

$_.name.replace('\','\\')

you can see how I used it in the recursive sample below

and the last question, about recursion,
we can make a recursive function in PowerShell to get all the subdirectories,
in the sample below the function does just call itself again for each subdirectory found. so that it wil recurse the complete subtree.


# recursive directory iterating using wmi
Function get-DirTree ($name) {
  (gwmi Win32_directory -filter "name = '$name'").getrelated('Win32_directory') |
    where {$_.name -match $name} |% {
      $_.name
      get-SubDirs $_.name.replace('\','\\')
    }
}


# Example 

MowPS>get-SubDirs c:\\foobar
c:\foobar\bar
c:\foobar\foo
c:\foobar\foo\bar


Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 8 comments

Sunday, May 21, 2006

 


PowerShell : Getting Subdirectories using WMI



In the new Microsoft.Public.Windows.PowerShell NewsGroup (we where shareing m.p.w.server.scripting),
In the Thread : gwmi to get folder and security info :

(the newsgroup is still so new that I can not find it in Google yes, so no link you have to fire up your newsreader and if you not have done yet subscribe to the NG (when done, keep hanging there you can learn a lot from the questions and aswers there .)
or use this link (http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.windows.powershell&cat=en_US_3750E87B-4971-4A5C-A537-45F5D7ABBECC&lang=en&cr=US ) for the Webbased version.

the question was why this command (changed to use the foobar example dir )took to long :

$folders = gwmi Win32_Directory Where {$_.Name -eq "C:\foobar"}

reason is first ALL the directorys on the computer (on all disks) get collected
and only then is the directory filtered out.

the answer is using a filter on the WMI query directly :

(gwmi Win32_directory -filter "name = 'c:\\foobar'")


but how did I come to this getting subdirectories as in the rest of the post was stated that Pete was working on a WMI translation of my PowerShell Import and Export a DirectoryTree and Security info example.

working from this former WMIsample.

PowerShell Import Shares and Security info From CSV

A good reason for this is that you want to do it remote, another reason he rewrote this was that he did not like use SDDL (no reason for using WMI as it would be also possible using the directorytree example ), but lets focus on the WMI and get subdirectory part

in the PowerShell Import and Export a DirectoryTree and Security info example I just use ls - recurse to enumerate to directories, in Share example we have a path to get the directory but how to do this in WMI ?

look at this example :

MowPS>(gwmi Win32_directory -filter "name = '$name'").getrelated()


Hidden                : False
Archive               : False
EightDotThreeFileName : c:\foobar\bar
FileSize              :
Name                  : c:\foobar\bar
Compressed            : False
Encrypted             : False
Readable              : True

Hidden                : False
Archive               : False
EightDotThreeFileName : c:\foobar\foo
FileSize              :
Name                  : c:\foobar\foo
Compressed            : False
Encrypted             : False
Readable              : True

Hidden                :
Archive               :
EightDotThreeFileName :
FileSize              :
Name                  : c:\
Compressed            :
Encrypted             :
Readable              : True

Caption          : Security settings of c:\foobar
ControlFlags     : 44052
Description      : Security settings of c:\foobar
OwnerPermissions : True
Path             : c:\foobar
SettingID        :
__GENUS          : 2
__CLASS          : Win32_LogicalFileSecuritySetting
__SUPERCLASS     : Win32_SecuritySetting
__DYNASTY        : CIM_Setting
__RELPATH        : Win32_LogicalFileSecuritySetting.Path="c:\\foobar"
__PROPERTY_COUNT : 6
__DERIVATION     : {Win32_SecuritySetting, CIM_Setting}
__SERVER         : Computer
__NAMESPACE      : root\cimv2
__PATH           : \\Computer\root\cimv2:Win32_LogicalFileSecuritySetting.Path="c:\\foobar"


the (gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated()
Method will get all the related classes, you can see that this includes all the directories that are related and also the Win32_LogicalFileSecuritySetting class (very hande as we needed that also.

to get all the relations as a WMI class use the GetRelationships Method
(gwmi Win32_directory -filter "name = '$name'").GetRelationships()

but there are 2 problems left first we need to split the security and the subdirectory info that is easy :

(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated('Win32_LogicalFileSecuritySetting')
(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated('Win32_directory')


but we also get the parent directory,
a diffence is that in this case the find directory is the GroupComponent part,

GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\"
we can filter on the GroupComponent but its hard to type
(with all the escaping)
But we can make use of some helperclasses for making the combined query.

For more info see : Lets Query WMI from MSH

it's from october last year so using Monad V2 but this is not changed much.
if you need to make more difficult WMI queries I think it still worth reading.

only to make the query in a GUI, I made an updatedversion that also takes pipelineinfo :

PowerShell out-DataGrid (MSH view-Object)

so to edit the query in a GUI you can do this :

new-object System.Management.RelatedObjectQuery | out-PropertyGrid

as I did a more detailed explainatin in that post I will not go into that,

but I will use a simpler workaround in Monad here
:
I just pipeline it to a where and check if the Path of the directories found contains the name of the current directory, if not it must be the root.

(gwmi Win32_directory -filter "name = '$name'").getrelated('Win32_directory') |? {$_.name -match $name}

so you can see that the WMI relations are very handy in this case to get from a directory to the directorysecurity and also gives a way to iterate subdirectories using WMI.

I hope this commandline examples make clear how this could help makeing the directoryscript using WMI.

Enjoy,

Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 5 comments

Wednesday, May 17, 2006

 


PowerShell basic parameter checking of a function



On my blog as I provide samples, I most of the time keep them as simple as possible,
without errorhandling or parameterchecking etc.I just focus on the target

as in the temperature converter function examples in last post,

http://mow001.blogspot.com/2006/05/powershell-celsius-fahrenheit.html

I did them as one-liners without any checking to keep it simple.

Jeffrey Snover, was so kind to leave a variation in the Comments, that does give usage info if you do not give a parameter (see example 2 below), mine would just convert 0 (see example 1 below)

this makes the script a bit more userfriendly, but what if a string is given ?

FooFoo32 ?

hmm, we should better catch that also then ...(that is why I let them out most of the time, to keep focussed at the "real" example ;-), but ok lets provide 2 other variations now, to make it up)

one way is to give typeinfo data to the parameter, so that this is checked by the function,
(see example 3 below)

so we also did catch wrong parameters now.
but this does not give the usage information,

so I did another veriation, in the last example, I removed the typeinfo to the parameter and used a Trap Statement, to provide the UsageInformation.(see example 4 below)

in the samples below I will show the different version and what does happen with different kinds of (wrong) input :

Examples :



# Example 1 
# My old Function :

function ConvertTo-Celsius ($Fahrenheit) {($Fahrenheit - 32) / 1.8}

# example

MowPS>ConvertTo-Celsius 77
25

# if you not specify a parameter $null -> 0 is calculated :

MowPS>ConvertTo-Celsius
-17.7777777777778

# example 2 : 
# Jeffrey Snover 's variation with some errorhandling

function ConvertTo-Fahrenheit ($Celsius=$(Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) {
  "{0:N1}" -f ($Celsius * 1.8 + 32)
}

function ConvertTo-Celsius ($Fahrenheit=$(Throw "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) {
  "{0:N1}" -f (($Fahrenheit - 32) / 1.8)


# output

# Usage information as no parameter is given, 

MowPS>ConvertTo-Fahrenheit
USAGE: ConvertTo-Fahrenheit -Celsius degrees
At line:1 char:48
function ConvertTo-Fahrenheit ($Celsius=$(Throw  <<<< "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) {

# but if a string is given :
 
MowPS>ConvertTo-Fahrenheit foo
foofoo32

# Example 3 
# we can catch this by adding typeinfo to the parameter :

function ConvertTo-Celsius ([decimal]$Fahrenheit=$(Throw "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) {
  "{0:N1}" -f (($Fahrenheit - 32) / 1.8)


# output

MowPS>ConvertTo-Celsius
USAGE: ConvertTo-Celsius -Fahrenheit degrees
At line:1 char:57
function ConvertTo-Celsius ([decimal]$Fahrenheit=$(Throw  <<<< "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) {

MowPS>ConvertTo-Celsius foo
ConvertTo-Celsius : Cannot convert value "foo" to type "System.Decimal". Error: "Input string was not in a correct format."
At line:1 char:18
+ ConvertTo-Celsius  <<<< foo

MowPS>ConvertTo-Celsius 77
25.0

# Example 4 :
# an other variation giving a custom message using TRAP :

function ConvertTo-Fahrenheit ($Celsius=$(Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) {
  trap{Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees"}
  $Celsius=[decimal]$Celsius
  "{0:N1}" -f ($Celsius * 1.8 + 32)
}

# output :

MowPS>ConvertTo-Fahrenheit foo
USAGE: ConvertTo-Fahrenheit -Celsius degrees
At line:2 char:13
+   trap{Throw  <<<< "USAGE: ConvertTo-Fahrenheit -Celsius degrees"}





hope this examples help in adapting other examples to scripts with some errorchecking,
(so lazy me can leave them out in my samples and say it is for readability ;-))

For more information about errorhandling and debugging in powershell see also this 7 part series on the PowerShell team blog about it :
Debugging Monad Scripts, Part 7 (Final): How Traps Work (links to all former parts are in this post)

Enjoy,
Greetings /\/\o\/\/

Tags :


posted by /\/\o\/\/
 0 comments

Saturday, May 13, 2006

 


PowerShell Celsius Fahrenheit converting



Just a quick one,
as I needed to convert some temperatures, I did this 2 functions,
no magic here, but still they can come handy ;-)

MowPS>function ConvertTo-Fahrenheit ($Celsius) {$Celsius * 1.8 + 32}

MowPS>ConvertTo-Fahrenheit 25
77

MowPS>function ConvertTo-Celsius ($Fahrenheit) {($Fahrenheit - 32) / 1.8}

MowPS>ConvertTo-Celsius 77
25


Enjoy,

Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 1 comments

Thursday, May 11, 2006

 


PowerShell : How Can I Rename Files Using File Names I've Written to a Text File?



and an other Hey Scripting Guy Item translation to PowerShell,

How Can I Rename Files Using File Names I've Written to a Text File?

import-csv names.csv %| {ren $_.old $_.new}

as this line (as remarked by Scott Hansellman), see Upgrading MSH, My first Windows PowerShell Commands

is a bit awkward opposed to ren *.msh *.ps1 in the CMD shell.

ls . *.msh -rec | rename-item -newname {$_.name -replace ".msh",".ps1"} -WhatIf

you can see, by this example, if you compare it to the VBscript version is not that bad ;-)

Here the commandline code of the sample I made for this :

# make testdir

MowPS>md ren


Directory: Microsoft.PowerShell.Core\FileSystem::C:

Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2006-05-11 5:06 PM ren

# create test files

MowPS>cd ren
MowPS>ls
MowPS>sc 001.jpg 'bla'
MowPS>sc 002.jpg 'bla'

# make List

MowPS>copycon
Old,New
001.jpg,foo.jpg
002.jpg,bar.jpg
^Z

cmdlet out-file at command pipeline position 1
Supply values for the following parameters:
FilePath: Names.csv

# CopyCon is a custom Function I like

MowPS>gc function:copycon
if ($args[0] -eq $null) {[system.console]::in.readtoend() out-file}
Else {[system.console]::in.readtoend() out-file $args[0]}

# the "script" itself

MowPS>import-csv names.csv %| {ren $_.old $_.new}

# Checking

MowPS>ls


Directory: Microsoft.PowerShell.Core\FileSystem::C:\ren


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2006-05-11 5:07 PM 5 bar.jpg
-a--- 2006-05-11 5:06 PM 5 foo.jpg
-a--- 2006-05-11 5:10 PM 88 Names.csv


Enjoy,

Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 1 comments
 


PowerShell make a drive of an UNC path



I see a lot of post lately about using wscript.Network to make a networkmapping,
like this :

$net = $(New-Object -Com WScript.Network)
$net.MapNetworkDrive("u:", "\\computer\share")

e.g. here :

Can you map a drive with PowerShell?

and here.

http://www.computerperformance.co.uk/ezine/ezine110.htm

but there is a more native way to do this on PowerShell,

*edit* but as Jacques (links to French blog janel) pointed out to me in the comments, this is only valid for the PowerShell so you can not use it to map a drive and use it from the CMD prompt or the GUI (what is a big difference ) but if you use it in the powershell it has a lot of power as naming the drive with more letters, a path, or mapping to a registrykey.

New-PSDrive HKCR Registry -root
HKEY_CLASSES_ROOT

or a registry path e.g. the PowerShell configuration

New-PSDrive PsConfig Registry -root
HKLM:\software\Microsoft\powershell\1\ShellIds

and ofcourse as mentioned an UNC path :

just type new-PsDrive and you can make an PowerShell drive of a Networkshare :

MowPS>new-psdrive

cmdlet new-psdrive at command pipeline position 1
Supply values for the following parameters:

Name: u
PSProvider: filesystem
Root: \\Computer\share

Name Provider Root
---- -------- ----
u FileSystem \\Computer\share

gr /\/\o\/\/
Tags :


posted by /\/\o\/\/
 2 comments

Wednesday, May 10, 2006

 


Some Powershell News



I did see some interesting PowerShell items a wanted to share

first this PowerShell Grammar overview.

then as there is a new PowerShell revision1.0.9567.1 (links still work / work again)
there also came 2 updated tools :

Tony did a new version of PowerShell Remoting

PowerShell Remoting version 0.1.1.7

Also Karl made a new version of PowerShell analyzer
Recompiled for new Windows PowerShell RC1 (Refresh version)

http://www.karlprosser.com/coder/?p=43

also I did see this post, Not bothering with MS newsgroups any more,
as I'm sometimes also a bit wondered why some questions are asked, they make it harder to anwer real questions, but then again they also give sometimes lead to nice and interesting reactions Can someone explain why we should need powerscript. and some humor so sometimes are still nice to follow.

as PowerShell gets his own newsgroup I things will get a bit better, as we also have to think about it that we are still "sharing" the NG with the other Server scripting languages, so not everybody is asking for the PowerShell posts also.

it does stay a difficult question, as I also remember how it was when I started with Monad, l but I still would recommed to follow microsoft.public.windows.server.scripting and later microsoft.public.windows.PowerShell as it still is a great source to get PowerShell info, and support.
b.t.w. I did see a discusion like this before in the comments on Lee Holmes blog before about what to post on his blog,
Question: Do you _not_ read the newsgroup?

what do you think about Newsgroups comparing other forms like the docs, Blogs, Sites, IRC ?

Enjoy,
Greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 2 comments

Monday, May 08, 2006

 


PowerShell Import and Export a DirectoryTree and Security info



While and after writing the PowerShell Export and Import shares scripts I got questions on how to do this for Directory Security in the NG thread that started me writing those 2 example scripts (Security and monad) ,

PowerShell Export Shares and Security info to CSV

PowerShell Import Shares and Security info From CSV

and I also got a comment from Pete Gomersall on the latter post, about doing this for Directories and recursing.

I already provided some info about win32_directory and the get-acl CDMlet in the Thread.

but I decided to write some example scripts for that also, but this Time I will not use WMI but the get-acl and set-acl commandlets.

Also I save the Security info in SDDL (Security Descriptor String Format) form to make the CSV file smaller, as Directories most of the time have much more and detailed ACL's as a Share does.

for more info about SDDL see : Security Descriptor String Format [Security] or do a Google Search on SDDL for more info.

(Note that SDDL does support "GA" GENERIC_ALL opposed to the .NET Enum see NG thread)



The provided scripts work like this :

MowPS>Export-DirTee c:\foobar

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-2


MowPS>Export-DirTee c:\foobar -r

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-2
C:\foobar\Bar                                     O:S-1-5-2
C:\foobar\Foo                                     O:S-1-5-2


MowPS>Export-DirTee c:\foobar -r | fl


Dir  : c:\foobar
SDDL : O:S-1-5-21-

Dir  : C:\foobar\Bar
SDDL : O:S-1-5-21-

MowPS>Export-DirTee c:\foobar -r dirSec.csv

Exporting to dirSec.csv

MowPS>rd c:\fooBar

Confirm

The item at C:\fooBar has children and the -recurse parameter was not specified. If you continue,
all children will be removed with the item. Are you sure you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): a

# all Directories are gone now :

MowPS>Export-DirTee c:\foobar -r

Path "c:\foobar" Not Found

# Let re-Import Them :

MowPS>Import-dirTree dirSec.csv
Creating c:\foobar
Setting Security on : c:\foobar

Creating C:\foobar\Bar
Setting Security on : C:\foobar\Bar

Creating C:\foobar\Foo
Setting Security on : C:\foobar\Foo

# and Yes the Security is back also :

MowPS>Export-DirTee c:\foobar

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-21-


MowPS>Export-DirTee c:\foobar -rec

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-21-
C:\foobar\Bar                                     O:S-1-5-21-
C:\foobar\Foo                                     O:S-1-5-21-


Note that this will not keep any files and does not touch the security on them, the Import script will just create the Directories if they do not exist and set the security on them, any existing files will not be touched, and will keep the current security.

and here the scripts :


# export-dirTree
#
# this Function will Export a directory Tree    
# complete with securityInfo to CSV  
#   
# /\/\o\/\/ 2006      
# http://mow001.blogspot.com 
 
Function Export-DirTree ($path,[switch]$recurse,$Outfile) {
  if (Test-Path($path)) {
    $DirInfo = @() 
    $DirInfo += $path | select @{e={$_};n='Dir'},
                               @{e={(get-acl $_).sddl};n='SDDL'}
    if ($recurse.IsPresent) {
      ls $path |? {$_.PsIsContainer} |% {
         $DirInfo += $_ | select @{e={$_.fullname};n='Dir'},
                                    @{e={(get-acl $_.fullname).sddl};n='SDDL'}
      }
    }

    if ($outFile){
      write-host "Exporting to $outFile"
      $DirInfo | export-csv $outFile
    }Else{
      $DirInfo
    }

  }Else{
    write-host "Path `"$path`" Not Found"
  }

}

# Import-DirTree

# This Function will Import the directories from a CSV file   
# made by Export-DirTree function complete with securityInfo  
#   
# /\/\o\/\/ 2006      
# http://mow001.blogspot.com 

Function Import-DirTree ($file) {
  $DirList = Import-Csv $file
  $DirList |% {
    Write-Host "Creating $($_.dir)"
    New-Item -type directory -path $_.dir | out-null
    Write-Host "Setting Security on : $($_.dir)"
    $acl = (get-acl $_.dir)
    $acl.SetSecurityDescriptorSddlForm($_.sddl)
    set-acl $_.dir $acl
    Write-Host ""
  }
}




For more info about the PowerShell ACL handling :

My Blog :

Adding a Simple AccesRule to a file ACL in MSH

More ACL and MSH on MSH for Fun

PowerShell for Fun blog :

http://mshforfun.blogspot.com/2005/12/play-with-acl-in-msh.html

MSH For Fun: Play with ACL in MSH (continued)

PowerShell For Fun: Combination Rights, Inheritance and ...

And this exelent article about the .NET 2.0 Security Classes

http://www.grimes.demon.co.uk/workshops/secWSNine.htm

Enjoy,

Greetings /\/\o\/\/

Tags :




posted by /\/\o\/\/
 3 comments

Thursday, May 04, 2006

 


PowerShell How Can I Query a Text File and Retrieve Records That Occurred on a Specific Date?



When I did see todays Hey, Scripting Guy! question (and more answer)

Hey, Scripting Guy!
How Can I Query a Text File and Retrieve Records That Occurred on a Specific Date?

I could not resist :

Hey, PowerShelled Scripting Guy!

import-csv employees.txt |? {$_.HireDate -eq '3/1/2006'}

but to be honest I also use ADO a lot with CSV files from PowerShell, but mostly with a Dataset and a DataAdapter,

# with import-csv function

MowPS>import-csv employees.txt |? {$_.HireDate -eq '3/1/2006'}

LastName               FirstName              Department            HireDate
--------               ---------              ----------            --------
Myer                   Ken                    Finance               3/1/2006
Ackerman               Pilar                  Finance               3/1/2006

# With ADO (and 2 helper functions)

MowPS>. connect-csv (pwd)

TABLE_NAME
----------
Employees#txt

MowPS>(get-DataTable "Employees#txt") |? {$_.HireDate -eq '3/1/2006'}

LastName               FirstName              Department            HireDate
--------               ---------              ----------            --------
Myer                   Ken                    Finance               3/1/2006 12:00:00 AM
Ackerman               Pilar                  Finance               3/1/2006 12:00:00 AM



in this case you see that it does not matter much, and for the ADO example I need 2 helper functions and it is outputting more or less doing the same, but in the second example I have the power of the .NET dataset and can make relations etc

for more info and the helper functions see :

working with CSV files in MSH (part one)

working with CSV files in MSH (part two)

And without the dataAdapter and dataset :

more Monad scripts, and a bit more CSV

enjoy,

gr /\/\o\/\/
Tags :


posted by /\/\o\/\/
 0 comments

Tuesday, May 02, 2006

 


PowerShell Import Shares and Security info From CSV



this Powershell scipt will import the shares from the CSV file created in the first part of this serie : PowerShell Export Shares and Security info to CSV .

For this example let's asume that we have 2 shares : Foo and Bar

so listing the shares will give this output :

MowPS>gwmi Win32_Share -filter 'type=0'

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo


on both we have everyone read and on the Share Bar I added MOW with Full Control

and did we run the script in the first post in this serie and exported them to a CSV file, the file would look like this :

MowPS>gc ShareInfo.csv

Name,Path,Description,User,Domain,SID,AccessMask,AceFlags,AceType
Bar,C:\Bar,,Everyone,Computer,S-1-1-0,1179817,0,0
Bar,C:\Bar,,Mow,Computer,S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx,2032127,0,0
Foo,C:\Foo,,Everyone,Computer,S-1-1-0,1179817,0,0


we can check the rights by using the FileSystemRights Enum :

MowPS>[System.Security.AccessControl.FileSystemRights]1179817

ReadAndExecute, Synchronize

MowPS>[System.Security.AccessControl.FileSystemRights]2032127

FullControl


(note share security is a bit different from filesystemsecurity so the reading is a bit different Replace Security on existing share using MSH)

now we want to re-create the shares

I will first show some parts of it from the CommandLine and then show the complete script (the compete script can be pasted to the commandline also) :

first we get the CSV file into an PSObject again

MowPS>$ShareList = Import-Csv ShareInfo.csv
MowPS>$sharelist


Name : Bar
Path : C:\Bar
Description :
User : Everyone
Domain : Computer
SID : S-1-1-0
AccessMask : 1179817
AceFlags : 0
AceType : 0

Name : Bar
Path : C:\Bar
Description :
User : Mow
Domain : Computer
SID : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx
AccessMask : 2032127
AceFlags : 0
AceType : 0

Name : Foo
Path : C:\Foo
Description :
User : Everyone
Domain : Computer
SID : S-1-1-0
AccessMask : 1179817
AceFlags : 0
AceType : 0


as we only can create the share one time but there could me more entries for a share in the script I loop only trough the shares on the commandline it looks like this :

MowPS>$sharelist | select -unique name,Path,Description

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo


you can see how get a list with every share only one time, so I can loop trough it to make the shares.

for each share in the loop I find only the rows of the second share and I do another loop (on the commandline Example I hardCoded Bar)

MowPS>$sharelist |? {$_.name -eq 'bar'}


Name : Bar
Path : C:\Bar
Description :
User : Everyone
Domain : Computer
SID : S-1-1-0
AccessMask : 1179817
AceFlags : 0
AceType : 0

Name : Bar
Path : C:\Bar
Description :
User : Mow
Domain : Computer
SID : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx
AccessMask : 2032127
AceFlags : 0
AceType : 0


Next I need to convert the textform SID into a Binary SID again,
therefore we can use the securityidentifier .NET class ( for more info see :
Get Binary SID in MSH (Share Security Update))

an commandline example for one SID :

MowPS>$sharelist[1].sid

S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx

MowPS>$sid = new-object security.principal.securityidentifier(($sharelist[1].sid))
MowPS>[byte[]]$ba = ,0 * $sid.BinaryLength
MowPS>$sid.GetBinaryForm($ba,0)
MowPS>$sid fl

BinaryLength : 28
AccountDomainSid : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx
Value : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx

MowPS>$ba
1
5
0
0
0
0
0
5
21
...
...


(there are also examples on my blog to translate the SID object to a user, try a search for SID on my blog for this)

the making of the trustee and ACE'es are the same as in the change share security sample so I will not further go into it here,

only you may note that I do this for the name property only :

$Trustee.get_Properties()['name'].set_Value($_.domain)

this is becouse there is a AliasProperty Name Defined that overrules the Original Name property of the win32_trustee Class in the typedata of PowerShell RC1, I will file this as a Bug on Connect.

the calling of the create method I did from an template I did create with the get-WmiMethodHelp script from this post : MSH get-WmiMethodHelp function Update 3 and did fill in.

note the use of $sd.PsObject.BaseObject
(if you forget this PowerShell will give a confusing error)

The Complete script looks like this :

# ImportShares.ps1 
# This script will Import the Shares from a CSV file  
# made by ExportShares.ps1 complete with securityInfo 
#  
# /\/\o\/\/ 2006     
# http://mow001.blogspot.com  

# Import the CSV file

$ShareList = Import-Csv ShareInfo.csv

# get the Unique Shares

$sharelist | select -unique name,Path,Description |% {

  $name = $_.name
  $path = $_.Path
  $description = $_.Description
  "Processing : $name $path $description"

  $sd = new-object system.management.managementclass Win32_SecurityDescriptor
  $sd.DACL = @()
  $ace = new-object system.management.managementclass Win32_ace
  $Trustee = new-object system.management.managementclass win32_trustee

  # Get all records in CSV file for the current share

  $sharelist |? {$_.name -eq $name} |% {

    # Convert SID to Binary version :

    $sid = new-object security.principal.securityidentifier($_.sid)
    [byte[]]$ba = ,0 * $sid.BinaryLength
    $sid.GetBinaryForm($ba,0)

    # Make Trustee Object

    $Trustee.Domain = $_.Domain
    $Trustee.get_Properties()['name'].set_Value($_.domain)
    $Trustee.SID = $ba

    # Make ACE Object

    $ace.AccessMask = $_.AccessMask
    $ace.AceType = $_.AceType
    $ace.AceFlags = $_.AceFlags
    $ace.trustee = $trustee.psobject.baseobject
 
    # Add ACE to the DACL

    $sd.DACL += $ACE.psObject.baseobject
  }

  $MC = new-object system.management.ManagementClass win32_share
  $InParams = $mc.GetMethodParameters('Create')

  # fill Parameters

  $InParams["Access"] = $sd.PsObject.BaseObject
  $InParams["Description"] = $_.Description
  $InParams["Name"] = $_.name
  $InParams["Password"] = [string]
  $InParams["Path"] = $_.Path
  $InParams["Type"] = 0

  $R = $mc.InvokeMethod('Create', $inParams, $Null)
  "Result : $($R.ReturnValue)"
 
}


And we can use it like this :


MowPS>gwmi win32_share -filter "type = 0"

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo

#delete shares

MowPS> ( gwmi win32_share -filter "name = 'foo'").delete()
MowPS> ( gwmi win32_share -filter "name = 'bar'").delete()

# shares gone

MowPS>gwmi win32_share -filter "type = 0"

#import shares

MowPS>.\ImportShares.ps1
Processing : Bar C:\Bar
Result : 0
Processing : Foo C:\Foo
Result : 0

# shares back

MowPS>gwmi win32_share -filter "type = 0"

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo

# if shares are there we get an error 22

MowPS>.\ImportShares.ps1
Processing : Bar C:\Bar
Result : 22
Processing : Foo C:\Foo
Result : 22

# check the security :

MowPS>$shareSec = gwmi Win32_LogicalShareSecuritySetting -filter "name='bar'"
MowPS>$shareSec.invokeMethod('GetSecurityDescriptor',$null,$null).descriptor.dacl % {
>> $_ select @{e={$share.name};n='Name'},
>> @{e={$share.Path};n='Path'},
>> @{e={$share.Description};n='Description'},
>> AccessMask,
>> AceFlags,
>> AceType,
>> @{e={$_.trustee.Name};n='User'},
>> @{e={$_.trustee.Domain};n='Domain'},
>> @{e={$_.trustee.SIDString};n='SID'}
>> }
>>


Name : Foo
Path : C:\Foo
Description :
AccessMask : 1179817
AceFlags : 0
AceType : 0
User : Everyone
Domain : Computer
SID : S-1-1-0

Name : Foo
Path : C:\Foo
Description :
AccessMask : 2032127
AceFlags : 0
AceType : 0
User : Mow
Domain : Computer
SID : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx

# and Yep this looks right, the seurity is back also.


you can see the shares are recreated with the right securitym and a return value is returned to check if it want OK (a 0 means OK a 22 share does exist allready).

if you want to know the other returnvalues you can look at the get-WmiMethodHelp script output also :


MowPS>get-WmiMethodhelp win32_share create
win32_share : create :


The Create method initiates sharing for a server resource. Only members of the Administrators or Account Operators local group or those wit
h Communication, Print, or Server operator group membership can successfully execute Create. The Print operator can add only printer queues
. The Communication operator can add only communication device queues. The method returns an integer value that can be interpretted as foll
ows:
0 - Successful completion.
2 - The user does not have access to the requested information.
8 - Unknown failure.
9 - The character or file system name is invalid.
10 - The value specified for the level parameter is invalid.
21 - The specified parameter is invalid.
22 - The share name is already in use on this server.
23 - The operation is invalid for a redirected resource. The specified device name is assigned to a shared resource.
24 - The device or directory does not exist.
25 - The share name does not exist.
Other - For integer values other than those listed above, refer to Win32 error code documentation.


create Parameters :

...
<snap>




Enjoy,

Greetings /\/\o\/\/
Tags :

PS. as Blogger had problems and my first post failed my post is messed up by BLogger,
so the formatting is messed up and you may miss some pipeline symbols (and and of lines when pasting, sorry for that,
but I RePasted the complete script again into the post so that should not give problems when pasted in the the powerShell


posted by /\/\o\/\/
 3 comments

Archives

October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   May 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006   December 2006  

$Links = ("PowerShell RC1 Docs"," PowerShell RC1 X86"," PowerShell RC1 X64"," Monad GettingStarted guide"," Monad Progamming Guide"," Monad SDK"," Monad videos on Channel 9"," MSH Community Workspace"," scripts.readify.net "," MonadSource"," www.reskit.net"," PowerShell Blog"," Under The Stairs"," computerperformance powershell Home"," proudlyserving"," MSH on wikipedia"," MSHWiki Channel 9"," Keith Hill's Blog"," Precision Computing"," PowerShell for fun"," MSH Memo (Japanese)"," monadblog")

find-blog -about "PowerShell","Monad" | out-Technorati.
find-blog -contains "","" | out-Technorati.
Google
 
Web mow001.blogspot.com

This page is powered by Blogger. Isn't yours?