Search code examples
powershellhistory

PowerShell's Clear-History doesn't clear history


Recently I had to run a command that unfortunately required me to type a password right on the command line.

Afterwards, I cleared my screen with "Clear", but also wanted to clear the command history so the offending command wouldn't show up in the session history. Unfortunately, the Clear-History cmdlet doesn't seem to actually do what its documentation claims - running Clear-History doesn't seem to have any impact on the session history whatsoever.

I can still see previous commands in the pop-up history menu, and scroll through old commands by pressing the up key. Here's a screengrab demonstrating the problem:

PowerShell clear history failure

I've verified with Get-Command that Clear-History is indeed executing the expected built-in PowerShell cmdlet.

I've tried a few variations, such as "Clear-History -count 10 -newest", all failing to show any effect. When I specify an exact history ID, such as "Clear-History -id 3", I receive an error like this:

Clear-History : Cannot locate history for Id 3.

Even if I can see command #3 on the screen.


Solution

  • tl;dr

    • There are two histories to clear:

      • PowerShell's own (Clear-History)
      • Additionally, in consoles (terminals), that of the PSReadLine module that is used for command-line editing by default in PowerShell v5+ ([Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory())
    • In versions 1.2+ of PSReadLine (verify with Get-Module PSReadLine) pressing Alt+F7 performs both calls for you, and therefore fully clears the in-session history.

      • However, it does not clear the saved history that has accumulated up to this point, so even the cleared session's history will resurface in future sessions.

      • To also clear the saved history, you have to manually delete the file in which the saved session is stored ((Get-PSReadlineOption).HistorySavePath), as discussed below, and as wrapped by the Clear-SavedHistory function in the bottom section.


    To complement CB.'s helpful answer and JVimes's helpful answer:

    • PowerShell's own history mechanism (Get-History, Clear-History) is host-independent, which is why - somewhat unexpectedly - you also need to clear the hosts's command history separately.

    • As for the console host's own history feature:

      • doskey-style history feature, before module PSReadline shipped with PowerShell (see below):

        • There is no saved history - a history is kept only for the duration of the current session.
        • Alt+F7 must be used to clear the console's history, with no (obvious) programmatic way to do it (in a cmd.exe console window you could use doskey /reinstall, but that doesn't work in PS).
        • CB.'s answer shows you how to simulate this keyboard combination; remember: this must be used in addition to Clear-History.
      • The PSReadline module comes with PowerShell v5 and v5.1 on Windows 10 and will also ship with Windows Server 2016, and also ships with the cross-platform Powershell (Core) v7+ edition; it replaces the doskey-style line-editing and command-history features with more sophisticated functionality; it is also possible to retrofit older Windows editions / PS versions (>= v3) versions with it, using the PowerShell Gallery (PSv3 and PSv4 must first install PowerShellGet).

        • Command history is now saved across sessions, in file
          (Get-PSReadlineOption).HistorySavePath.
        • [Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory() can be used to clear the current session's history (note that v1.2+ also supports Alt+F7 for interactive clearing of the current history).
          • CAVEAT: With PSReadline's default history-saving style, SaveIncrementally, any sensitive commands have already been saved by the time to you call [Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory(), and will reappear in the next session.
          • The only way to handle this is to remove the saved-history file, as demonstrated in JVimes's answer which, however, invariably wipes out the entire history.
          • IF you set up your profile to call Set-PSReadlineOption -HistorySaveStyle SaveAtExit every time a session starts - the setting apparenly does NOT "stick" by itself - you should be able to get away with only calling [Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory() (in addition to Clear-History) without also having to delete the saved-history file, in which case you won't lose your saved history from previous sessions. HOWEVER, AS OF v2.1.0 (the latest as of this writing), SaveAtExit is BROKEN ALTOGETHER - no history is saved at all; see https://github.com/lzybkr/PSReadLine/issues/262

    The following advanced function bundles all commands necessary to clear the command history (both for PowerShell itself and the console), both for doskey-style and PSReadline-module PowerShell console windows:

    Note:

    • Because it's (currently) the only safe option, PSReadline's saved-history file is deleted as well, which means the entire history, including from previous sessions, is cleared.

    • Therefore, a confirmation prompt is shown by default.

    <#
    # .SYNOPSIS
    #  Clears the command history, including the saved-to-file history, if applicable.
    #>
    function Clear-SavedHistory {
      [CmdletBinding(ConfirmImpact='High', SupportsShouldProcess)]
      param(    
      )
    
      # Debugging: For testing you can simulate not having PSReadline loaded with
      #            Remove-Module PSReadline -Force
      $havePSReadline = ($null -ne (Get-Module -EA SilentlyContinue PSReadline))
    
      Write-Verbose "PSReadline present: $havePSReadline"
    
      $target = if ($havePSReadline) { "entire command history, including from previous sessions" } else { "command history" } 
    
      if (-not $pscmdlet.ShouldProcess($target))
      {
            return
      }
    
      if ($havePSReadline) {
        
        Clear-Host
    
        # Remove PSReadline's saved-history file.
        if (Test-Path (Get-PSReadlineOption).HistorySavePath) { 
          # Abort, if the file for some reason cannot be removed.
          Remove-Item -EA Stop (Get-PSReadlineOption).HistorySavePath 
          # To be safe, we recreate the file (empty). 
          $null = New-Item -Type File -Path (Get-PSReadlineOption).HistorySavePath
        }
    
        # Clear PowerShell's own history 
        Clear-History
    
        # Clear PSReadline's *session* history.
        # General caveat (doesn't apply here, because we're removing the saved-history file):
        #   * By default (-HistorySaveStyle SaveIncrementally), if you use
        #    [Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory(), any sensitive
        #    commands *have already been saved to the history*, so they'll *reappear in the next session*. 
        #   * Placing `Set-PSReadlineOption -HistorySaveStyle SaveAtExit` in your profile 
        #     SHOULD help that, but as of PSReadline v1.2, this option is BROKEN (saves nothing). 
        [Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory()
    
      } else { # Without PSReadline, we only have a *session* history.
    
        Clear-Host
        
        # Clear the doskey library's buffer, used pre-PSReadline. 
        # !! Unfortunately, this requires sending key combination Alt+F7.
        # Thanks, https://stackoverflow.com/a/13257933/45375
        $null = [system.reflection.assembly]::loadwithpartialname("System.Windows.Forms")
        [System.Windows.Forms.SendKeys]::Sendwait('%{F7 2}')
    
        # Clear PowerShell's own history 
        Clear-History
    
      }
    
    }