# 如何有效清空 PowerShell 中 PSReadline 的所有歷史紀錄

我的 [如何打造一個華麗又實用的 PowerShell 命令輸入環境](/post/2021/11/24/PowerShell-prompt-with-Oh-My-Posh-and-Windows-Terminal) 文章 ([影片在此](https://www.youtube.com/watch?v=MA_gIbs6P1c)) 幫助了不少人調整他手中的 PowerShell 命令列環境，其中有個功能是在打字的時候可以提示你先前打過的所有命令，非常方便且實用。但上周有同事問了我一個問題，他曾經在 PowerShell 中輸入過一些敏感資料，但是又找不到清除歷史紀錄的方法，問我該怎麼辦才好。這篇文章我將分享解決之道！

### 清除 PowerShell 當前工作階段的歷史紀錄 (Current session memory)

你每次開啟一個 PowerShell 視窗，就會建立一個工作階段 (Session)，而每個工作階段都會維護一份輸入命令的歷史紀錄。

由於 Windows 10 開始，預設 PowerShell 會自動載入 [PSReadLine](https://learn.microsoft.com/en-us/powershell/module/psreadline/about/about_psreadline?view=powershell-7.2&WT.mc_id=DT-MVP-4015686) 模組，大大強化了 PowerShell 命令列環境的命令輸入能力。你可以使用以下兩個命令管理輸入命令的歷史紀錄：

1.  取得歷史紀錄 ([Get-History](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-history?view=powershell-7.2&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    Get-History
    ```
    
    你也可以用內建的 `h` 別名 (Alias)，直接輸入 `h` 就可以查詢歷史紀錄：
    
    ```powershell
    h
    ```
    
    以下是顯示在畫面上的範例：
    
    ```
      Id CommandLine
      -- -----------
       1 dir
       2 cls
       3 Get-Alias
    ```
    
2.  執行歷史命令 ([Invoke-History](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-history?view=powershell-7.2&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    Invoke-History 3
    ```
    
    你也可以用內建的 `r` 別名 (Alias)，直接輸入 `r <Id>` 就可以查詢歷史紀錄：
    
    ```powershell
    r 3
    ```
    
3.  清除歷史紀錄 ([Clear-History](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/clear-history?view=powershell-7.2&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    Clear-History
    ```
    
    > 另外有個 `Alt-F7` 鍵盤快速鍵，當你按下之後，並不會清除所有歷史紀錄，而是讓你不能用 ⬆ 方向鍵載入之前輸入過的命令！
    

### 清除所有已儲存的 PowerShell 歷史記錄 (Persisted history file)

其實管理一個工作階段的歷史紀錄不怎麼實用，因為 PSReadLine 預設會記憶你有史以來所有輸入過的命令，所以你下次重開 PowerShell 又可以搜尋到所有的歷史紀錄！事實上，你在 PowerShell 視窗輸入的所有命令，全部都記錄到一個文字檔中，你可以用以下命令查出該檔案的路徑：

```powershell
(Get-PSReadlineOption).HistorySavePath
```

預設應該是這個路徑：

```powershell
$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
```

我的這個檔案有 13,370 行，可以想像裡面包含了多少東西，但這個機制非常單純，這反而可以讓我想出更多種應用：

1.  你可以手動清除所有不想保留在歷史紀錄中的命令
    
    例如你曾經輸入過的金鑰或密碼，都應該找出來清空。
    
2.  你可以加入所有你希望未來可以隨時查詢到的命令
    
    有很多常見的命令，可以整理一遍，這樣之後要輸入命令就會變的非常實用！ 👍
    
    不過 PSReadLine 有個 `MaximumHistoryCount` 選項設定，定義能載入的最大歷史命令的數量：
    
    ```powershell
    (Get-PSReadLineOption).MaximumHistoryCount
    ```
    
    這個設定的預設值為 4,096 筆，因此當你開啟 PowerShell 之後，並不會真的載入 13,370 行命令，而是只會載入命令最後面的 4,096 行而已。若你要加入一些自己希望在未來可以查到的命令，建議直接加到該檔案的最後面！
    

### 從已儲存的 PowerShell 歷史記錄檔案中刪除部分記錄

假設你想從 50 萬行的 `ConsoleHost_history.txt` 檔案中，刪除所有命令中包含 `Run-MyAgent.ps1` 字串的記錄，你可以這樣執行：

```powershell
$hist = Get-Content (Get-PSReadLineOption).HistorySavePath ; $hist | Where-Object { $_ -notmatch 'Run-MyAgent.ps1' } | Set-Content (Get-PSReadLineOption).HistorySavePath
```

> 注意: 上面兩個命令我串成一行，必須用 `;` 分隔，不能用 `&&` 喔，否則你的歷史紀錄會全部被清空！

若要用**正則表達式**來比對，可以這樣寫。假設你想將所有 `$hist` 開頭的命令記錄都刪除，可以執行以下腳本：

```powershell
$path = (Get-PSReadLineOption).HistorySavePath
$hist = Get-Content $path
$regex = [regex]'^\$hist' # 可用更複雜的 regex
$hist | Where-Object { -not $regex.IsMatch($_) } | Set-Content $path
```

### 相關連結

-   [如何打造一個華麗又實用的 PowerShell 命令輸入環境 | The Will Will Web](/post/2021/11/24/PowerShell-prompt-with-Oh-My-Posh-and-Windows-Terminal)
-   [打造一個華麗又實用的 PowerShell / Bash 命令輸入環境 - YouTube](https://www.youtube.com/watch?v=MA_gIbs6P1c)
-   [about PSReadLine - PowerShell | Microsoft Docs](https://learn.microsoft.com/en-us/powershell/module/psreadline/about/about_psreadline?view=powershell-7.2&WT.mc_id=DT-MVP-4015686)
-   [F7 is the greatest PowerShell hotkey that no one uses any more. We must fix this. - Scott Hanselman's Blog](https://www.hanselman.com/blog/f7-is-the-greatest-powershell-hotkey-that-no-one-uses-any-more-we-must-fix-this)
-   [PowerShell's Clear-History doesn't clear history - Stack Overflow](https://stackoverflow.com/a/38807689/910074)
