I want a clock in the top right corner of the terminal using powershell similar if not identical to the bash script provided below.
Bash snippet:
while true; do echo -ne "\e[s\e[0;$((COLUMNS-7))H\e[30;47m$(date +'%H:%M:%S')\e[0m\e[u"; sleep 1; done &
And i want something similar in PowerShell. I tried this:
Clear-Host
function Show-Clock {
$width = [console]::WindowWidth
$height = [console]::WindowHeight
$x = $width - 8 # Width for "HH:MM:SS"
$y = 0
while ($true) {
$time = (Get-Date).ToString("HH:mm:ss")
[console]::SetCursorPosition($x, $y)
Write-Host "$time" -BackgroundColor White -ForegroundColor Black -NoNewline
Start-Sleep -Seconds 1
}
}
Show-Clock
And it works but you cant use the terminal while its running which i want to. Any help is appreciated!
Edit 1:
Now i used Start-Process -NoNewWindow
in order to mimic the ampersands (&) functionality but that didnt work either:
Clear-Host
function Show-Clock {
$width = [console]::WindowWidth
$height = [console]::WindowHeight
$clockPositionX = $width - 8 # Width for "HH:MM:SS"
$clockPositionY = 0
while ($true) {
$time = (Get-Date).ToString("HH:mm:ss")
[console]::SetCursorPosition($clockPositionX, $clockPositionY)
Write-Host "$time" -BackgroundColor White -ForegroundColor Black -NoNewline
Start-Sleep -Seconds 1
}
}
Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command 'Show-Clock'" -NoNewWindow
While you could make your Start-Process -NoNewWindow
approach work if you added saving and restoring the current cursor location, it has two downsides (on Windows; doesn't affect Unix-like platforms):
Pressing Ctrl+C in the session doesn't just abort any foreground operation, but also the time-displaying process.
Conversely, when you exit
the session, the time-displaying process lingers and keeps the console window open, giving the impression of a hang; pressing Ctrl+C terminates that process too and closes the window.
The best solution is to use a thread (parallel PowerShell runspace) in which to run the time-displaying code, which is most easily achieved with the Start-ThreadJob
cmdlet.[1]
This cmdlet comes with PowerShell (Core) 7.
In Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1) it can be installed on demand, using, e.g., Install-Module ThreadJob -Scope CurrentUser
The following, which is a translation of your bash
command,[2] works in both PowerShell editions, in both regular console windows and in Windows Terminal, and also on Unix-like platforms (assuming that the terminal of interest supports saving and restoring the cursor position via ANSI escape sequences):[3]
# See below for an alternative that doesn't require Start-ThreadJob.
$null = Start-ThreadJob {
$esc = [char] 0x1B
while ($true) {
[Console]::Write((
'{0}[s{0}[0;{1}H{0}[30;47m{2}{0}[m{0}[u' -f
$esc,
([Console]::WindowWidth - 7),
[datetime]::Now.ToString('HH:mm:ss')
))
Start-Sleep -Seconds 1
}
}
Note:
Receive-Job
, the [Console]
.NET class provides direct access to the caller's console (terminal).On Windows PowerShell, if you cannot / don't want to install the module that contains Start-ThreadJob
, you can use the PowerShell SDK directly, as follows:
$scriptBlock = {
$esc = [char] 0x1B
while ($true) {
[Console]::Write((
'{0}[s{0}[0;{1}H{0}[30;47m{2}{0}[m{0}[u' -f
$esc,
([Console]::WindowWidth - 7),
[datetime]::Now.ToString('HH:mm:ss')
))
Start-Sleep -Seconds 1
}
}
$null = [powershell]::Create().AddScript($scriptBlock).BeginInvoke()
[1] Note that PowerShell's regular background jobs, which are created with Start-Job
, are not an option in this case, because they create a child process rather than a thread, and that child process isn't connected to the caller's console.
For a juxtaposition of background jobs (Start-Job
) and the generally preferable thread jobs (Start-ThreadJob
), see this answer.
[2] It's tempting to try to run your original Bash command directly from PowerShell, via bash -c '...'
, but that doesn't actually work: the time prints in the wrong place (at the current prompt on Unix-like platforms) or not visibly at all (when calling bash.exe
from outside WSL on Windows).
[3] On macOS, the built-in Terminal.app application does not support the cursor-location saving/restoring ANSI esscape sequences, but popular third-party alternative iTerm.app does. My guess is that most Linux terminals do have the necessary support.