# 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]*)')
$m[0].Value
}
}
}
In this version, i have added functionality to expand "about_[tab]" manual files.
Now you can do;
switch -regex ($lastWord)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.
{
#
# 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]*)')
$m[0].Value
}
}
}
# ...
<Type>
<Name>System.Diagnostics.Process</Name>
<Members>
... Other membersets and properties ...
<ScriptProperty>
<Name>Service</Name>
<GetScriptBlock>
$svc = @(, (Get-WmiObject Win32_Service -Filter "ProcessId='$($this.Id)'" | Group-Object ProcessId).Group)
if ($svc[0] -eq $null) { "N/A" } else { $svc }
</GetScriptBlock>
</ScriptProperty>
</Members>
</Type>
$svc = @(, (Get-WmiObject ...
if ($svc[0] ...
[^_^]PS[142]>$a = @(1)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.
[^_^]PS[143]>$a | Get-Member
TypeName: System.Int32
Name MemberType Definition
---- ---------- ----------
CompareTo Method System.Int32 Compa
...
[^_^]PS[144]>, $a | Get-Member
TypeName: System.Object[]
...
[^_^]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...[^_^]PS[2]>Measure-Command { Get-Process | Select-Object name,id,service }Tags : Monad msh PowerShell
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
# 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]; ">"
}
# 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] `
'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
$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 {
"$($matches[1])::$($_.name)"
}
}
break;
}
# Remove Brackets from typename (/\/\o\/\/)
'(\[.*\])=(\w*)' {
"$($matches[1].replace('[','').replace(']',''))"
break;
}
# 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
[void]($global:dtAssemblies.Columns.Add('name',[string]))
[void]($global:dtAssemblies.Columns.Add('DC',[int]))
[void]($global:dtAssemblies.Columns.Add('NS',[string]))
$assemblies = [appdomain]::CurrentDomain.GetAssemblies()
[void] ($assemblies | % {$i = 0} {
$i++;
[int]$assemblyProgress =
($i * 100) / $assemblies.Length
Write-Progress Caching "$assemblyProgress" `
-perc $assemblyProgress
$types = $_.GetTypes()
$types |
foreach {$j = 0} {
$j++;
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
$global:dtAssemblies.rows.add("$_",$dc,$ns)
}
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)]"}
$res
}
}
break;
}
# 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
}
break;
}
# 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
}
}
break;
}
###
### 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
}
}
break;
}
# Handle variable name expansion...
'(.*^\$)(\w+)$' {
$prefix = $matches[1]
$varName = $matches[2]
foreach ($v in Get-Childitem ('variable:' + $varName + '*')) {
$prefix + $v.name
}
break;
}
# 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' `
$cmdlet.Definition)[0]
}
# 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 }
}
break;
}
} # EO switch
} # EO Process
}
$global:[tab]So why not just extend tabExpansion function.
$local:[tab]
$script:[tab]
$private:[tab]
'(.*^\$)(\w+):(\w*)$' {I am not sure what to call "$function:", "$variable:" and "$env:" so I just called them as "Type".
$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
}
}
break;
}
begin {Now, not only the following works,
$_Method = [Management.Automation.PSMemberTypes] 'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
$_ScopeNames = @("global", "local", "script", "private")
}
$global:[tab]But also, you can expand variable names for
$local:[tab]
$script:[tab]
$private:[tab]
$env:[tab]A simple extension to TabExpansion makes life on Console much less painful... ;)
$function:[tab]
$variable:[tab]
Experimenting with a different format of blogs...