
Sometimes you write this fancy batch script that does a bunch of stuff, and you want to have it print out some status information as it’s doing its thing.
Sometimes this fancy script is doing a lot and there’s going to be a lot of stuff printed, so it would be nice if you could overwrite the previous bit of text. Basically, you want a progress bar or progress indicator of some sort.
There are a few ways of doing this, and one involves manipulating the $Host.UI.RawUI.CursorPosition
values. That needs “a lot” of code for something that you really don’t want to write a lot of code for.
There are also the oldskool typewrite control characters, however. Like the Carriage Return, `r
in PowerShell, which does pretty much the same thing.
So this bit of code, for example, prints everything out on a single line, even though it’s doing that a hundred times:
100..0 | % { write-host "`r- Items to process: $($_)".PadRight(25) -nonewline; sleep -milliseconds 20 }
The magic is in this line:
write-host "`r- Items to process: $($_)".PadRight(25)
Note the “`r” at the beginning of the line. This will reset the cursor to the beginning of the current line, printing the text behind it over any text already present on that line.
Do this in a loop, and you keep writing over the previously printed text.
This also explains the PadRight()
statement, which makes sure that there are spaces added to the end of the line to erase any characters left over if the previous line was longer than the current one.
This happens a number of times in this case, as we’re counting from 100 to 0. I know there are smarter ways to fix this, but this works just fine right here (KISS).
Here’s another example using the CR trick. An actual character based progress bar. Just copy-paste and run it in a shell to see the effect:
1..20 | % { write-host ("#"*$_ + "|" * (20-$_) + "`r") -nonewline ; sleep -Milliseconds 200 }; ""
The following example is a bit more complex. It displays a spinner for longer running operations using a set of characters.
# Animation object to keep state.
$global:animation = @{ Chars = "-\|/"; Ix = 0; Counter = 0 }
# Animate one step every 500 calls. Lower the number for a faster animation.
function Animate() {
$a = $global:animation
if (($a.Counter % 500) -eq 0) {
Write-Host " $($a.Chars[$a.Ix])`r" -NoNewline
$a.Ix = ($a.Ix + 1) % $a.Chars.Length
}
$a.Counter++
}
# Usage example. Call the animation in a loop.
$largeImages = ls *.jpg -r | where { $_.length -gt 100000; animate }
There’s also the official Write-Progress
PowerShell commandlet to show a progress bar on the screen. You might want to check that out too. I’m not a fan of it myself, because it tends to act strange when you scroll in your shell window, but for more complex status updates it can be really handy.
I hope this helps to make your scripts a bit more informative (or fun) when running long jobs.