powershellterminalclock

Powershell clock in top right corner


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

Solution

  • 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):

    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]

    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:


    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.