-
Notifications
You must be signed in to change notification settings - Fork 1
/
Invoke-CommandShortener.ps1
169 lines (135 loc) · 7.46 KB
/
Invoke-CommandShortener.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<#
.SYNOPSIS
Shortens and simplifies a PowerShell script block by replacing command names with aliases and using shortest parameter aliases.
.DESCRIPTION
The Invoke-CommandShortener function takes a PowerShell script block as input and performs the following tasks:
1. Splits the input script block into individual lines, removing empty lines and delimiters.
2. Identifies command delimiters in the input script block.
3. Parses the input script block into an Abstract Syntax Tree (AST).
4. Extracts a list of command elements and their associated parameters from the AST.
5. Creates a list of command information, including aliases and parameters.
6. Replaces command names with their aliases and parameter names with their shortest aliases.
7. Returns the modified script block.
.PARAMETER InputScriptBlock
Specifies the PowerShell script block that you want to shorten and simplify.
.EXAMPLE
Invoke-CommandShortener -InputScriptBlock {Foreach-Object -Process {"Blub"}
Get-ChildItem -Path C:\Temp -Hidden;Cls }
Output:
% -Process {"Blub"}
ls -Path C:\Temp -h;Cls
# $shortenedScript will contain the modified script block with shortened command names and aliases.
.EXAMPLE
Invoke-CommandShortener -InputScriptBlock {Get-Process | Where-Object { $_.CPU -gt 50 }}
Output:
ps | ? { $_.CPU -gt 50 }
# $shortenedScript will contain the modified script block with shortened command names and aliases.
.NOTES
File Name : Invoke-CommandShortener.ps1
Author : Christian Ritter
Prerequisite : PowerShell v3.0
version : 0.3
.LINK
Online version: https://github.com/HCRitter/PSCommandShortener
#>
function Invoke-CommandShortener {
param (
[scriptblock]$InputScriptBlock
)
# Split the input script block into individual lines, removing empty lines and delimiters
$ScriptblockText = New-Object -TypeName "System.Collections.ArrayList"
$scriptBlockText.AddRange(@($InputScriptBlock.ToString().Trim() -split "(?<=;|`n|\|)" | ForEach-Object { $_ -replace "[;`n|]" } | Where-Object { $_ -match '\S' }))
# Identify command delimiters in the input script block
$commandDelimiters = $InputScriptBlock.ToString() | Select-String -Pattern "(\n|\||;)" -AllMatches | ForEach-Object { $_.Matches.Value }
# Parse the input script block into an Abstract Syntax Tree (AST)
$ast = [System.Management.Automation.Language.Parser]::ParseInput($InputScriptBlock.ToString(), [ref]$null, [ref]$null)
# Extract a list of command elements and their associated parameters from the AST
$commandElementList = $ast.FindAll({$args[0].GetType().Name -like 'CommandAst'}, $true) | ForEach-Object {
[pscustomobject]@{
Cmdlet = $_.CommandElements[0].Value
Parameters = $_.CommandElements.ParameterName
}
}
# Create a list of command information, including aliases and parameters
$list = foreach ($commandElementListItem in $commandElementList) {
$command = Get-Command -Name $commandElementListItem.Cmdlet
$commandAlias = $null
# Determine if the command is an alias and resolve it if necessary
switch ($command) {
{$PSItem.Commandtype -eq "Alias"} {
$commandAlias = $commandElementListItem.Cmdlet
$command = Get-Command $PSItem.ResolvedCommand
}
Default {
# Find the shortest alias for the command if it's not an alias itself
try {
$commandAlias = ((Get-Alias -Definition $commandElementListItem.Cmdlet -ErrorAction Stop).DisplayName.ForEach({
$_.Split("-")[0]
})) | Sort-Object -Property Length | Select-Object -first 1
}
catch {
$commandAlias = $null
}
}
}
$parameters = [ordered]@{}
# Match command parameters with their aliases and select the shortest alias or unique match
foreach ($commandElementListItemParameterItem in $commandElementListItem.Parameters) {
switch ((Get-Command $command.Name | Select-Object -ExpandProperty ParameterSets).Parameters | Where-Object { ($_.Name -eq $commandElementListItemParameterItem) -or ($_.Aliases.contains($commandElementListItemParameterItem)) }) {
{($commandElementListItemParameterItem -eq $PSItem.Aliases) -or ($commandElementListItemParameterItem -eq $PSItem.Name) -and (-not [string]::IsNullOrEmpty($PSItem.Aliases))} {
# If the parameter is an alias, select the shortest alias
$parameters[$PSitem.Name] = $($PSItem.Aliases | Sort-Object -Property Length | Select-Object -first 1)
}
Default {
# Find the shortest unique parameter match
$ShortestUniqueParameterMatch = ""
# Iterate through each character in the parameter string
foreach ($Char in $commandElementListItemParameterItem.ToCharArray()) {
$ShortestUniqueParameterMatch += $Char
# Count how many parameters start with the current match
$count = ((Get-Command $command.Name | Select-Object -ExpandProperty ParameterSets).Parameters.Name | Where-Object { $_ -like "$ShortestUniqueParameterMatch*" }).Count
if ($count -eq 1) {
break # Exit the loop when count equals 1 (shortest unique match found)
}
}
$parameters[$PSitem.Name] = $ShortestUniqueParameterMatch
}
}
}
[PSCustomObject]@{
CommandAlias = $commandAlias
CommandName = $command.Name
Parameters = $parameters
}
}
# Initialize the final script block text
$finalScriptBlockText = ""
# Process each line of the script block
for ($i = 0; $i -lt $scriptBlockText.Count; $i++) {
# Replace command names with their aliases or implied 'Get-' if available
switch -Wildcard ($list[$i].CommandName) {
{ -not [string]::IsNullOrEmpty($list[$i].CommandAlias) } {
$scriptBlockText[$i] = $scriptBlockText[$i] -replace $list[$i].CommandName, $list[$i].CommandAlias
}
{ $_ -like "Get-*" } {
$scriptBlockText[$i] = $scriptBlockText[$i] -replace $list[$i].CommandName, ($_ -replace "Get-")
}
}
switch ($list[$i].Parameters.GetEnumerator()) {
# Replace parameter names with their shortest aliases
{ -not [string]::IsNullOrEmpty($_.Value) } {
$scriptBlockText[$i] = $scriptBlockText[$i] -replace $_.Key, $_.Value
}
}
# Append the modified line to the final script block text
$finalScriptBlockText += $scriptBlockText[$i]
# Append the command delimiter if present
if ($i -lt $commandDelimiters.Count) {
$finalScriptBlockText += $commandDelimiters[$i]
}
}
# Replace line breaks with CRLF and remove extra spaces
$finalScriptBlockText = $finalScriptBlockText -replace '(?<!\r)\n', "`r`n" -replace ' {2,}', ' '
# Create and return the modified script block
return $([scriptblock]::Create($finalScriptBlockText))
}