I am opening a new powershell tab in Windows Terminal via a script, which launches a specific command. When I press the "up" arrow, that command I've sent in the script is not persisted in powershell's history. The only commands persisted are those in my history, and those that have occurred after the tab has opened and run.
Is there a way to persist this command (in this case lazygit
) to powershell's history so I can access it by simply using the "up" arrow once the app is closed?
wt -d "d:/git/" powershell -NoExit lazygit
Preface:
Commands submitted via the PowerShell CLI (powershell.exe
for Windows PowerShell, pwsh
for PowerShell (Core) 7) are not recorded in PowerShell's command history.
The answer below discusses a solution to compensate for that.
Mathias, in a comment on the question, correctly points out that adding a command to PSReadLine
's command history as part of a PowerShell CLI call is not an option, because PSReadLine
, the bundled module that facilitates the interactive command-line editing experience, isn't yet initialized at the time the command passed to the CLI executes.
However, you can manually append a command to PSReadLine
's history file before making a CLI call, which will then be offered as the first one in the history recall, e.g.:
$cmd = 'lazygit'
$cmd | Add-Content (Get-PSReadLineOption).HistorySavePath
wt -d 'd:/git' powershell -NoExit $cmd
Note:
(Get-PSReadLineOption).HistorySavePath
returns the full file path of the file in which the command history is stored, via the Get-PSReadLineOption
cmdlet, and Add-Content
appends to its contents.
With a simple command like lazygit
, the variable value can be used as-is for appending to the history as well as part of the wt.exe
+ powershell.exe
command line.
By contrast, commands that contain ;
and/or "
characters require escaping for command-line use; see below.
# Use a sample command that involves both the following embedded chars: ; "
$cmd = 'Write-Output "Hello, World"; Get-Date'
# Save the command as-is to PSReadLine's history.
$cmd | Add-Content (Get-PSReadLineOption).HistorySavePath
# For use on the command line the embedded chars. need *escaping*.
# (The -d option for setting the working dir. is omitted for brevity.)
wt.exe powershell -NoExit -Command (
$cmd -replace ';', '\;' -replace '"', ('{0}"' -f ('\' * (3, 1)[[int] $IsCoreClr]))
)
Note:
The above is a cross-edition solution that works when calling from both Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1) and from PowerShell (Core) 7 (the modern, cross-platform, install-on-demand edition).[1]
If you only need to support one or the other edition, the solution is simpler:
To only support PowerShell (Core) 7:
wt.exe powershell -NoExit -Command ($cmd -replace '[;"]', '\$&')
To only support Windows PowerShell:
wt.exe powershell -NoExit -Command (
$cmd -replace ';', '\;' -replace '"', '\\\"'
)
Notably, aside from the Windows PowerShell bug that needs to be compensated for (again, see footnote [1]), the unexpected need for escaping ;
and "
(already escaped as \"
behind the scenes) inside a "..."
-enclosed argument received by wt.exe
is a arguably a bug in the latter.
With respect to ;
, the unexpected need to \
-escape it inside "..."
has been reported before as something that needs documenting, namely in GitHub docs issue #763, but the attempt to address it fell short, unfortunately, as summarized here.
Sadly, given Microsoft's commitment to backward compatibility, it us unlikely that the current behavior will be fixed.
[1] The automatic $IsCoreCLR
variable is used to distinguish between the two editions. Strictly speaking, PowerShell (Core) 7 versions up to v7.2.x behaved the same as Windows PowerShell with respect to embedded "
chars., but these versions are no longer supported. The extra \
instances are needed when calling from Windows PowerShell due to a long-standing bug - see this answer - that won't be fixed in that edition, which will see only fixes that are both security-critical and don't break backward compatibility.