::::::::: PowerShell :::::::::
Wednesday, June 28, 2006
  Windows Desktop Search Powershell Cmdlet by Sean McLeod
Great stuff!
A cmdlet, Get-WDS (in SnapIn) that searches WDS(Windows Desktop Search) indexes is born...

Mr. Sean McLeod(the author) Newsgroup post on microsoft.public.windows.PowerShell can be found here.

Instead of me explaining what the cmdlet is about and how it works, go to The Code Project article, Windows Desktop Search Powershell Cmdlet

Tags :
Tuesday, June 20, 2006
  Extending TabExpansion function Part 3
EDIT*: According to /\/\o\/\/'s advice(refer to comment's section), i have modified the previous source to use Get-Help instead of Get-ChildItem to retrieve available about_ help file names. (BTW, i will be modifying this script yet, again, so that "about_" would expand only when "Get-Help" or "Help" is already typed...(not to mess up File name expansion in $PsHome directory)

  # Handle "about_*" help manuals
'\s*(about_[\w|\d]*)' {
$matched = $matches[1]
$helpFileNames = Get-Help $matched | Sort-Object Name | Select-Object Name

if ($helpFileNames.Length -ne $null) {
$helpFileNames | foreach {
# extract manual name(NOTE:same RegEx pattern)
$m = [RegEx]::Matches($_.Name, '\s*(about_[\w|\d]*)')

In this version, i have added functionality to expand "about_[tab]" manual files.

Now you can do;
I was a bit annoyed to type long manual names like about_ubiquitous_parameters and about_regular_expression (which is a manual i read often) all the time so decided to become *lazy*...

When you do get-help about_alias, See Also section mentions about_variable manual.
If you try "about_v[Tab]" it would not work because there is no manual file named about_variable.help.txt in $pshome.

Now it should be apparent now how I have implemented my functionality.
It basically checks for manual file name that matches "about_blah*" against manual file names on $PSHOME directory.

switch -regex ($lastWord)
# Other functionalities...

# Handle "about_*" help manuals
'\s*(about_[\w\d]*)' {
$matched = $matches[1]
# Search for manual file name in $PSHOME
$helpFileNames = Get-ChildItem "$pshome/$($matched)*.help.txt" | Sort-Object Name | Select-Object Name

if ($helpFileNames -ne $null) {
$helpFileNames foreach {
# extract manual name(NOTE:same RegEx pattern)
$m = [RegEx]::Matches($_.Name, '\s*(about_[\w\d]*)')

# ...

Add above piece of code to "TabExpansion.ps1", go to wherever you have saved the file and ". .\TabExpansion.ps1" and you are good to go.
For previous blog posts(mine or /\/\o\/\/'s)on "TabExpansion", go check out "http://del.icio.us/powershell/TabExpansion"

Tags :
Wednesday, June 14, 2006
  Domain Names
EDIT*: great, both domains are working properly~

Uhm, Just bought two domain names.
Here is how I will forward my new domains.
They are easier to remember && type(as well as names being shorter...)
I think it will take about 2 ~ 3 days for them to take effect...

Tags :
Tuesday, June 13, 2006
  Getting Services associated with a Process via PowerShell (.NET Type Extension)
On a recent Microsoft.Public.Windows.PowerShell NG (Newsgroup) posts, Andrew Watt has asked for an "Equivalent [cmdlet] for tasklist /svc".

Luckily, Abhishek has posted a blog entry concerning that very problem a couple of weeks ago as "Getting Services associated with a Process via PowerShell" on May 18, 2006.

According to Abhishek, "the Cmdlet implementation relies on System.Diagnostics.Process class in .Net which unfortunately does not expose this information", so he has utilized WMI object Win32_Service to retrieve service information associated with a process.

Abhishek has approached to the problem with a function, plist, to iterate each System.Diagnostics.Process object instance type and match each process against service information retreived through Win32_Service, and the append a NoteProprety to each "System.Diagnostics.Process" object instances.

But then, I just wanted more intuitive way(well i just wanted to mess around...) to retrieve "Service" information associated with a process id and .. Ta~da~... There was types.ps1xml...
One of the beauties of PowerShell is that, you can extend .NET types as you want. (Anyways, for more links concerning Type Extension feature and articles, go to http://del.icio.us/powershell/TypeExtension...)

This time, I have yet again, tried to extend another .NET type, System.Diagnostics.Process as I did in previous post( [MSH] Extending System.Security.SecureString ).

... Other membersets and properties ...
$svc = @(, (Get-WmiObject Win32_Service -Filter "ProcessId='$($this.Id)'" | Group-Object ProcessId).Group)
if ($svc[0] -eq $null) { "N/A" } else { $svc }

Ok, now let's see. What's going on here?
In $pshome\types.ps1xml, I am extending System.Diagnostics.Process type (System.Diagnostics.Process) to include a ScriptProperty () which contains a script block () to retrieve Service information associated with current Process's id ($this.Id) from Win32_Service WMI object.

The reason I have added the "if" statement on the second line in is because when you run "tasklist /svc", if there is no service(s) associated with a process ID, it is returned as "N/A".

There are two gotchas, here that you have to be careful of.
  1. $svc = @(, (Get-WmiObject ...
  2. if ($svc[0] ...
For the first case, I am actually including a null object to force an array with one item to be interpreted as an array with multiple items.
To illustrate the point, let's see the following case.
[^_^]PS[142]>$a = @(1)
[^_^]PS[143]>$a | Get-Member
TypeName: System.Int32
Name MemberType Definition
---- ---------- ----------
CompareTo Method System.Int32 Compa
[^_^]PS[144]>, $a | Get-Member
TypeName: System.Object[]
Although I have declared $a as an array of one item, "Get-Member" will return "System.Int32" , which is the type of the array item(1), as its TypeName for $a although most of people would expect an array type to be returned. Anyways, there was this NG post or a blog entry that dealt with this problem and I will try to update the blog later on for links concerning this problem.

For the second problem, well, I was checking for "$svc.Length -lt 1" but it turns out that, $svc.Length is always equal to or great than 1 because $svc will always contain a null object.(this is not that clear of an explanation... since I haven't understood this clearly, yet)

Why did I even bother going through the gotchas?
Well, if i hadn't used "@(, (Get-Wmi..." and used just "@(Get-Wmi..." then what you might get is something as ugly as

[^_^]PS[10]>gps svchost | select name,id,service | ft -auto

Name Id Service
---- -- -------
SVCHOST 972 {DcomLaunch, TermService}
SVCHOST 1072 \\IT03\root\cimv2:Win32_Service.Name="RpcSs"
SVCHOST 1204 \\IT03\root\cimv2:Win32_Service.Name="Dnscache"
SVCHOST 1264 {Alerter, LmHosts, SSDPSRV, WebClient}
SVCHOST 1284 {AppMgmt, AudioSrv, BITS, Browser...}
SVCHOST 3540 \\IT03\root\cimv2:Win32_Service.Name="stisvc"
SVCHOST 3772 \\IT03\root\cimv2:Win32_Service.Name="usnsvc"

where you see a full type name for Service property with only ONE service associated thereof.

The reason for the "if" is hmm well, if you run "tasklist /svc", for process Id's with no associated service(s), it returns "N/A" so I just wanted to mimic the behavior...

Well, that's it folks...
Ah, lastly, I have to tell you that running "Get-Process | Select-Object name,id,service" takes quite a long time(about 4 seconds!!! compared to 0.3 & 0.2 seconds by "tasklist /svc" and "plist" function). It is defniately not an efficient way to retrieve Service information associated with a process id. Extend your type and have fun at your own risk... ;)

[^_^]PS[2]>Measure-Command { Get-Process | Select-Object name,id,service }

Days : 0
Hours : 0
Minutes : 0
Seconds : 4
Milliseconds : 198
Ticks : 41988791
TotalDays : 4.85981377314815E-05
TotalHours : 0.00116635530555556
TotalMinutes : 0.0699813183333333
TotalSeconds : 4.1988791
TotalMilliseconds : 4198.8791

[^_^]PS[3]>Measure-Command { tasklist /svc }

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 244
Ticks : 2440259
TotalDays : 2.82437384259259E-06
TotalHours : 6.77849722222222E-05
TotalMinutes : 0.00406709833333333
TotalSeconds : 0.2440259
TotalMilliseconds : 244.0259

[^_^]PS[4]>Measure-Command { plist * | Format-Table name,id,service }

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 341
Ticks : 3419322
TotalDays : 3.95754861111111E-06
TotalHours : 9.49811666666667E-05
TotalMinutes : 0.00569887
TotalSeconds : 0.3419322
TotalMilliseconds : 341.9322

Tags :
Sunday, June 11, 2006
  "Prompt" function
EDIT2*: Well, That weird out-of-order registration of commands in history is not so funky any more after reading Bruce Payette's post on Microsoft.Public.Windows.PowerShell NG...
"Command that's creating a nested prompt level is complete only after you exit the nested level"...

This is kind of funky...
Photobucket - Video and Image Hosting
When you look at the output of Get-History in the 6th prompt, Start-NewScope are registered into history after Exit... and moreover, after each Exit, history counts have incremented by 2 instead of 1...

Uhm, I am kind of bored so I am gonna just post my "Prompt" function source in my "Profile.Ps1".
I have 3 parts in my PowerShell prompt
  1. Nested Prompt Level([^_^] <- when the nested prompt level is "0")
  2. "PS" in red(Uhm, I forgot where i saw that flashy red MSH> Prompt last year for the first time...)
  3. History Count([2], not count of last history but the history count of what you will run)
Here is the screenshot of my prompt

Photobucket - Video and Image Hosting

I have found history count(refer to Lee Holmes' blog on "A History Browsing Prompt" for extracting history count, there are few quirks here and there.) to be quite useful as I find myself copying and pasting from console and history(by the way, check out How Keith Hill got around the problem with a SelectHistory script)

Below is my "Prompt" function and "Start-NewScope"(function invoked on the screenshot) can be found on PowerShell Team blog http://blogs.msdn.com/monad/archive/2006/04/15/576996.aspx

# reference: For History Count, 
# Author: DBMwS
# http://www.leeholmes.com/blog/CommentView,guid,0a552b85-2abe-4528-be41-d55762eb41de.aspx
# and Jeffrey Snover's comment on NG for nested prompt(or Start-NewScope)
function prompt
if (($NestedPromptLevel -eq $null) -or ($NestedPromptLevel -eq 0)) {
# I want to see more friendly Prompt... so chose "^_^" over "0"
$private:level = "^_^"
} else { $private:level = $NestedPromptLevel }

$private:h = @(Get-History)
$private:nextCommand = $private:h[$private:h.Count - 1].Id + 1

Write-Host -NoNewline -ForeGroundColor Blue [$private:level]
Write-Host -NoNewline -ForeGroundColor Red PS;
Write-Host -NoNewLine -ForeGroundColor Cyan [$private:nextCommand]; ">"

I would like to address why i am checking for ($NestedPromptLevel -eq $null) in the "if" statement(According to Jeffrey Snover, "BTW - every time you enter a nested prompt, $NESTEDPROMPTLEVEL is incremented so I include this information in my prompt", but I could find out about it on "get-help about_automatic_variables", thought).

At the time I wrote this function(I am sorry. I have to admit that I almost blatantly copied and pasted from Lee Holmes' blog...), when the powershell started, $NestedPromptLevel was set to a null instead of "0" but I think it seems to have been fixed in RC1 Update.

Tony (Msh For Fun) has a blog entry on how to modify your prompt function the way you like it(like displaying time, and colorizing your prompt, evening randomizing prompt the color...) and what Prompt function is about and why you should bother at all... Great stuff.~

Prompt related links can be found on http://del.icio.us/powershell/Prompt(I have only 3 links so far but I will be tagging more later on.)

Tags :
Thursday, June 08, 2006
  Extending TabExpansion function Part 2
EDIT2*:Pastebin.com site's been down for a while so pasted the colorized source on the bottom...

EDIT*: Modified the source a bit but no functional changes made
Simply changed name of Script variables($_Method, $_ScopeNames) in "begin" blocks to $Script:Method and $Script:ScopeNames respectively according Mow's suggestion.
It can be found here

On completion of PowerShell Tab Completion Part 4, /\/\o\/\/ has suggested me that I should do multilevel scoped variable completion.

I had to cheat a bit by taking Mow's piece of code and turning it into a function for a reuse.
The source is on PowerShell.Pastebin.com.

In my version of TabCompletion, Mow's Multilevel variable completion functionality is in a function called "MultiLevelPropertyExpansion" in "begin" block.
Since list of functionalities are listed on PowerShell Tab Completion Part 4 already, I will just mention one tiny improvement.

In previous version, tab expansion occurred only on the first level
$global:Host.U[tab] -> $global:Host
$global:Host.UI.R[tab] -> $global:Host.UI

But now, multi-level expansion works, as well for scoped variables
$global:Host.u[tab] -> $global:Host.UI
$global:Host.UI.r[tab] -> $global:Host.UI.RawUI

$global:hos[tab].u[tab].r[tab].g[tab] -> $global:Host.UI.RawUI.get_BackgroundColor(

Uhm, "TabExpansion.ps1"
# TabExpansion.ps1
# Version 0.4
# Replacement of default TabExpansion function
# /\/\o\/\/ 2006

function TabExpansion {
# This is the default function that gets called for tab expansion.
# Edited by /\/\o\/\/ from the original to handle :
# - Cached tab completion on types (janel / mow).
# - Methods and properties of types
# - shows get_ methods
# - MultiLevel variable Tab Completion
# - Bracet Removal
# Edited by DBMwS: Added Progressbar and Scoped variable name expansion
# - MultiLevel variable Tab Completion for Scoped variables
# - Changed $_* variables to $Script:*

param($line, $lastWord)

begin {
$Script:Method = [Management.Automation.PSMemberTypes] `
$Script:ScopeNames = @("global", "local", "script", "private")

function MultiLevelPropertyExpansion {
param($parent, $child)

$private:explodedChild = $child.split('.')
$private:level = $explodedChild.Count
$private:value = $parent
if ($private:level -gt 1) {
$private:value += ".$($explodedChild[0..($level - 2)])"
$private:pattern = $private:explodedChild[($level -1)] + '*'

# /\/\o\/\/ removed : -and $n -notmatch '^[ge]et_'
# to get get_ methods on WMI and AD objects
$private:result = @()
$private:inputObject = '$' + $private:value
invoke-expression "Get-Member -InputObject $inputObject" |
Sort-Object name | where {$_.name -like $pattern } |
foreach {
$private:result += @{
MemberType = $_.MemberType;
Name = $_.Name;
Value = $private:value;

return $private:result


process {
switch -regex ($lastWord)
# Handle methods of Types (/\/\o\/\/).
# E.g.) [DateTime]::F[tab]
'(\[.*\])::(\w*)' {
invoke-expression "$($matches[1]) | gm -static" |
where { $n = $_.Name;
$n -like "$($matches[2])*" -and `
$n -notmatch '^([gs]et|op)_'} |
foreach {
if ($_.MemberType -band $Script:Method) {
"$($matches[1])::$($_.name)" + '('
} Else {

# Remove Brackets from typename (/\/\o\/\/)
'(\[.*\])=(\w*)' {

# Cache and Handle namespace and TypeNames (/\/\o\/\/) ..
'\[(.*)' {
$matched = $matches[1]
# only the first time Fill a DataTable with
# Typenames,namespaces and dotCount (level)
if (!($global:dtAssemblies)) {
$global:dtAssemblies = New-Object System.Data.Datatable
$assemblies = [appdomain]::CurrentDomain.GetAssemblies()
[void] ($assemblies | % {$i = 0} {
[int]$assemblyProgress =
($i * 100) / $assemblies.Length
Write-Progress Caching "$assemblyProgress" `
-perc $assemblyProgress

$types = $_.GetTypes()
$types |
foreach {$j = 0} {
if (($j % 500) -eq 0) {
[int]$typeProgress = ($j * 100) / $types.Length
Write-Progress "Adding types" "$typeProgress" `
-perc $typeProgress -id 1

$dc = $_.fullName.split(".").count - 1
$ns = $_.namespace
Write-Progress "Adding types" "100" -perc 100 -id 1

# actual tab completion
$dots = $matches[1].split(".").count - 1
switch ($dots) {
0 {"[System","[Microsoft"}
Default {
$res = @()
$res += $global:dtAssemblies.select(
"ns like '$($matched)%' and dc = $($dots + 1)") |
Select-Object -Unique ns |
foreach {"[$($_.ns)"};
$res += $global:dtAssemblies.select(
"name like '$($matched)%' and dc = $dots") |
foreach {"[$($_.name)]"}

# Handle property and method expansion (MultiLevel added /\/\o\/\/)...
'\$(\w+)\.(.*)' {
# parent variable name e.g.) $host.ui.-> parent is "host"
$parent = $matches[1];
# child variable name e.g.) $host.ui.rawui-> child is "ui.rawui"
$child = $matches[2]

MultiLevelPropertyExpansion $parent $child |
foreach {
$output = "$" + $_["Value"] + '.' + $_["Name"]
if ($_["MemberType"] -band $Script:Method) { $output += "(" }
return $output


# Handle expansions for both "Scope Variable Name"
# and "Type Variable Names" (DbmwS)
'(.*^\$)(\w+):(\w+)(\.(.*))*$' {
# "function", "variable", "alias", "env:", etc...
$type = $matches[2];
# E.g.) '$' + 'function'
$prefix = $matches[1] + $type;
# e.g. in '$function:C', $typeName will be 'C'
$typeName = $matches[3];
# e.g. in '$global:host.ui.rawui', child will be 'ui.rawui'
$child = $matches[5];

# Scope Variable Name Expansion for $global:, $private:, $script:, $local:
if ($Script:ScopeNames -contains $type) {
if ($child) {
# Handle *multi-level* property and method expansion
MultiLevelPropertyExpansion $typeName $child |
foreach {
$output = $prefix + ":" + $_["Value"] + '.' + $_["Name"]
# Append "(" for method
if ($_["MemberType"] -band $Script:Method) { $output += '('}
return $output
} else { # Expand scope variable name
foreach ($scopeVariable in
(Get-Variable "$($typeName)*" -Scope $type |
Sort-Object name)) {
$prefix + ":" + $scopeVariable.Name
} else {
# Type name expansion for $function:, $variable, $env:, etc
foreach ($t in
(Get-ChildItem ($type + ":" + $typeName + '*') |
Sort-Object name)) {
$prefix + ":" + $t.Name


### Default functions below this line

# Handle property and method expansion...
'\$(\w+)\.(\w*)' {
$variableName = $matches[1]
$val = Get-Variable -value $variableName
$pat = $matches[2] + '*'
Get-Member -inputobject $val |
where { $n = $_.name; `
$n -like $pat -and $n -notmatch '^([gs]et|op)_' } |
foreach {
if ($_.MemberType -band $Script:Method) {
# Return a method...
'$' + $variableName + '.' + $_.name + '('
} else {
# Return a property...
'$' + $variableName + '.' + $_.name

# Handle variable name expansion...
'(.*^\$)(\w+)$' {
$prefix = $matches[1]
$varName = $matches[2]
foreach ($v in Get-Childitem ('variable:' + $varName + '*')) {
$prefix + $v.name

# Do completion on parameters (original) ...
'^-([\w0-9]*)' {
$pat = $matches[1] + '*'

# extract the command name from the string
# first split the string into statements and pipeline elements
# This doesnt handle strings however.
$cmdlet = [regex]::Split($line, '[|;]')[-1]

# Extract the trailing unclosed block
if ($cmdlet -match '\{([^\{\}]*)$') {
$cmdlet = $matches[1]

# Extract the longest unclosed parenthetical expression...
if ($cmdlet -match '\(([^()]*)$') {
$cmdlet = $matches[1]

# take the first space separated token of the remaining string
# as the command to look up. Trim any leading or trailing spaces
# so you dont get leading empty elements.
$cmdlet = $cmdlet.Trim().Split()[0]

# now get the info object for it...
$cmdlet = @(Get-Command -type 'cmdlet,alias' $cmdlet)[0]

# loop resolving aliases...
while ($cmdlet.CommandType -eq 'alias') {
$cmdlet = @(Get-Command -type 'cmdlet,alias' `

# expand the parameter sets and emit the matching elements
foreach ($n in ($cmdlet.ParameterSets |
Select-Object -expand parameters)) {
$n = $n.name
if ($n -like $pat) { '-' + $n }
} # EO switch
} # EO Process

Tags :
Sunday, June 04, 2006
  Extending TabExpansion function
EDIT:* Changed "new" to "New-Object"(Thanks Martin and Mow) in the source on PasteBin.com and jEdit IDE comments, "#{{{", "#}}}" are gone.
Moreover, there is a little bit of change on Mow's tab expansion on PowerShell Tab Completion 2, that when caching for loaded assembly type names occur, progress bars(with help from /\/\o\/\/ again~) are displayed. So when you try out [System.m[tab], you will see two progress bars during caching. But it actually seems to slow down caching process a bit

EDIT2:* Screenshot: Showing progressbars while caching assembly type names to speed up TabExpansion.
The following happens the first time and the first time only(well that is until you actually delete $global:dtAssemblies) during first Type Name expansion [Sys[tab]

EDIT3:* Modified TabExpansion http://powershell.pastebin.com/764311.
Thanks, justin for pointing out the regex problem~

Inspired by /\/\o\/\/'s PowerShell Tab Completion 1 & 2 and was playing around with him on #powershell on FreeNode, I just decided to expand the tabExpansion function a bit more(with tips from Mow on IRC chatroom for Sorting and expanding partial variable names).

In the second part of Mow's PowerShell Tab Completion Part 2, Mow has saved cached assembly type names in $global:dtAssemblies.

Default tabExpansion function does not handle variable names with scope identifier appeneded. So the following will not work

So why not just extend tabExpansion function.
While at it, let's support "$variable:", "$env:" and "$function:" as well since they have the same variable name matching patterns.

'(.*^\$)(\w+):(\w*)$' {
$type = $matches[2]; # function, variable, etc.. that are not scopes
$prefix = $matches[1] + $type; # $ + function
$typeName = $matches[3]; # e.g. in '$function:C', value will be 'C'
if ($_ScopeNames -contains $type) {
# Scope Variable Name Expansion
foreach ($scopeVariable in
(Get-Variable "$($typeName)*" -Scope $type Sort-Object name)) {
$prefix + ":" + $scopeVariable.Name
} else {
# Type name expansion($function:, $variable, $env: ,etc)
foreach ($t in (Get-ChildItem ($type + ":" + $typeName + '*') Sort-Object name)) {
$prefix + ":" + $t.Name
I am not sure what to call "$function:", "$variable:" and "$env:" so I just called them as "Type".
In the above code snippet, '(.*^\$)(\w+):(\w*) matches "$function:", "$env:" or anything like "$" + "foo" + ":" and variable names with scope name appended are expanded differently from the ones with types.(<- well how should i be expressing this in clearer terms...) Scope names are checked with if ($_ScopeNames -contains $type) and "$_ScopeNames" is an array containing "global", "local", "script" and "private" and "$_ScopeNames" is declared in "begin{}" block of TabExpansion function like the following (All Regular Expression checks are processed inside "process{}" block.)

begin {
$_Method = [Management.Automation.PSMemberTypes] 'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
$_ScopeNames = @("global", "local", "script", "private")
Now, not only the following works,

But also, you can expand variable names for

A simple extension to TabExpansion makes life on Console much less painful... ;)
You can find the full source of my TabExpansion function, which includes sources posted on Mow's blogs "PowerShell Tab Completion 1 & 2", on powershell.pastebin.com

Tags :
Let's get lazy with PowerShell!

Location: Flushing, NY, United States

Experimenting with a different format of blogs...

10/01/2005 - 11/01/2005 / 11/01/2005 - 12/01/2005 / 12/01/2005 - 01/01/2006 / 01/01/2006 - 02/01/2006 / 02/01/2006 - 03/01/2006 / 03/01/2006 - 04/01/2006 / 04/01/2006 - 05/01/2006 / 05/01/2006 - 06/01/2006 / 06/01/2006 - 07/01/2006 / 07/01/2006 - 08/01/2006 / 08/01/2006 - 09/01/2006 / 10/01/2006 - 11/01/2006 / 11/01/2006 - 12/01/2006 /

Powered by Blogger