Skip to content

Instantly share code, notes, and snippets.

@KirkMunro
Last active August 29, 2015 14:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KirkMunro/97892e7d95541dcef0e3 to your computer and use it in GitHub Desktop.
Save KirkMunro/97892e7d95541dcef0e3 to your computer and use it in GitHub Desktop.
Hacking PowerShell command history
# IMPORTANT NOTE: This hack has evolved, and HistoryPx is now a full PowerShell module
# that is hosted on GitHub and can be found here: github.com/KirkMunro/HistoryPx
# First set up some interesting hacks
New-Module -Name HistoryPx -ScriptBlock {
$PSModule = $ExecutionContext.SessionState.Module
$global:__ = $null
$global:MaximumDetailedHistoryCount = 50
$global:PSDefaultParameterValues['Out-Default:OutVariable'] = 'global:__'
$commandHistory = [ordered]@{}
$previousOutput = $null
$onCommandInvokeBreakpoint = Set-PSBreakpoint -Variable __ -Mode Write -Action {
# When inside a variable breakpoint action, access the variable directly to get the old value,
# and use Get-Variable -Name $_.Variable -ValueOnly to get the new value
$success = $? -eq $true
$variableValue = (Get-Variable -Name $_.Variable -Scope Global -ValueOnly).Clone()
if ($variableValue -ne $null) {
$output = $variableValue
$previousHistoryInfo,$historyInfo = Get-History -Count 2
if ($script:previousOutput -eq $output) {
$output = $null
} else {
$sortA = @($script:previousOutput | Sort-Object)
$sortB = @($output | Sort-Object)
if ($sortA.Count -eq $sortB.Count) {
$equivalent = $true
for ($index = 0; $index -lt $sortA.Count; $index++) {
if ($sortA[$index] -ne $sortB[$index]) {
$equivalent = $false
break
}
}
if ($equivalent) {
$output = $null
} else {
$script:previousOutput = $output
}
} else {
$script:previousOutput = $output
}
}
$script:commandHistory.Add($historyInfo.Id.ToString(),[pscustomobject]@{
PSTypeName = 'DetailedHistoryInfo'
Id = $historyInfo.Id
CommandLine = $historyInfo.CommandLine
Duration = $(if ($historyInfo.ExecutionStatus -eq [System.Management.Automation.Runspaces.PipelineState]::Completed) {$historyInfo.EndExecutionTime - $historyInfo.StartExecutionTime})
ExecutionStatus = $historyInfo.ExecutionStatus
Success = $success
Errors = @($global:Error.where{$_.InvocationInfo.HistoryId -eq $historyInfo.Id})
Output = $(if ($output -ne $null) {$output | Where-Object {$_.PSTypeNames -notcontains 'DetailedHistoryInfo'}})
})
while ($script:commandHistory.Count -gt $global:MaximumDetailedHistoryCount) {
$script:commandHistory.RemoveAt(0)
}
}
}
Update-TypeData -TypeName DetailedHistoryInfo -DefaultDisplayPropertySet Id,CommandLine,Duration,Success
function Get-HistoryDetail {
[CmdletBinding()]
[OutputType('DetailedHistoryInfo')]
param()
foreach ($key in $commandHistory.Keys) {
$commandHistory[$key]
}
}
$PSModule.OnRemove = {
Remove-PSBreakpoint -Breakpoint $onCommandInvokeBreakpoint
$global:PSDefaultParameterValues.Remove('Out-Default:OutVariable')
Remove-Variable -Scope Global -Name MaximumDetailedHistoryCount
Remove-Variable -Scope Global -Name __
}
} | Import-Module
# Now generate some command history
1
2
'Fish'
gsv blah
3 > $null
gps -id $pid
get-eventlog -foo bar
[void]3
4
$true
# And show the detailed history output. Intriguing, no? :)
Get-HistoryDetail | ft Id,CommandLine,Success,Output,Errors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment